mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
Compare commits
17 Commits
2025-09-15
...
2025-10-06
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a6fda4daf | ||
|
|
1aea1467da | ||
|
|
3c01be29c4 | ||
|
|
24f4fbad82 | ||
|
|
30cbc41298 | ||
|
|
150e8ef43d | ||
|
|
4b91e985ac | ||
|
|
fdae128cec | ||
|
|
11e1330758 | ||
|
|
aa0eb4bedf | ||
|
|
141ed72693 | ||
|
|
62067aefd3 | ||
|
|
b7d9d7d9ae | ||
|
|
67958ec791 | ||
|
|
b666c52df9 | ||
|
|
6ead82154e | ||
|
|
ca95366219 |
@@ -3,3 +3,6 @@ rustflags = "-C link-arg=/STACK:8000000"
|
||||
|
||||
[target.'cfg(all(target_os = "windows", not(target_env = "msvc")))']
|
||||
rustflags = "-C link-args=-Wl,--stack,8000000"
|
||||
|
||||
[target.wasm32-unknown-unknown]
|
||||
rustflags = ["--cfg=getrandom_backend=\"wasm_js\""]
|
||||
|
||||
13
.github/dependabot.yml
vendored
13
.github/dependabot.yml
vendored
@@ -1,13 +1,10 @@
|
||||
# Keep GitHub Actions up to date with GitHub's Dependabot...
|
||||
# https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot
|
||||
# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
- package-ecosystem: cargo
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
groups:
|
||||
github-actions:
|
||||
patterns:
|
||||
- "*" # Group all Actions updates into a single larger pull request
|
||||
schedule:
|
||||
interval: weekly
|
||||
|
||||
24
.github/workflows/ci.yaml
vendored
24
.github/workflows/ci.yaml
vendored
@@ -122,7 +122,7 @@ jobs:
|
||||
- windows-2025 # TODO: Switch to `windows-latest` on 2025/09/30
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: clippy
|
||||
@@ -181,7 +181,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
target: i686-unknown-linux-gnu
|
||||
@@ -251,10 +251,10 @@ jobs:
|
||||
- windows-2025 # TODO: Switch to `windows-latest` on 2025/09/30
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
- name: Set up the Windows environment
|
||||
@@ -273,7 +273,7 @@ jobs:
|
||||
- name: build rustpython
|
||||
run: cargo build --release --verbose --features=threading ${{ env.CARGO_ARGS }},jit
|
||||
if: runner.os != 'macOS'
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
- name: run snippets
|
||||
@@ -316,7 +316,7 @@ jobs:
|
||||
name: Check Rust code with rustfmt and clippy
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt, clippy
|
||||
@@ -324,7 +324,7 @@ jobs:
|
||||
run: cargo fmt --check
|
||||
- name: run clippy on wasm
|
||||
run: cargo clippy --manifest-path=wasm/lib/Cargo.toml -- -Dwarnings
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
- name: install ruff
|
||||
@@ -357,7 +357,7 @@ jobs:
|
||||
env:
|
||||
NIGHTLY_CHANNEL: nightly
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
@@ -379,7 +379,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
@@ -390,12 +390,12 @@ jobs:
|
||||
wget https://github.com/mozilla/geckodriver/releases/download/v0.36.0/geckodriver-v0.36.0-linux64.tar.gz
|
||||
mkdir geckodriver
|
||||
tar -xzf geckodriver-v0.36.0-linux64.tar.gz -C geckodriver
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
- run: python -m pip install -r requirements.txt
|
||||
working-directory: ./wasm/tests
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v5
|
||||
with:
|
||||
cache: "npm"
|
||||
cache-dependency-path: "wasm/demo/package-lock.json"
|
||||
@@ -440,7 +440,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
target: wasm32-wasip1
|
||||
|
||||
8
.github/workflows/cron-ci.yaml
vendored
8
.github/workflows/cron-ci.yaml
vendored
@@ -18,6 +18,8 @@ jobs:
|
||||
codecov:
|
||||
name: Collect code coverage data
|
||||
runs-on: ubuntu-latest
|
||||
# Disable this scheduled job when running on a fork.
|
||||
if: ${{ github.repository == 'RustPython/RustPython' || github.event_name != 'schedule' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
@@ -44,6 +46,8 @@ jobs:
|
||||
testdata:
|
||||
name: Collect regression test data
|
||||
runs-on: ubuntu-latest
|
||||
# Disable this scheduled job when running on a fork.
|
||||
if: ${{ github.repository == 'RustPython/RustPython' || github.event_name != 'schedule' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
@@ -73,6 +77,8 @@ jobs:
|
||||
whatsleft:
|
||||
name: Collect what is left data
|
||||
runs-on: ubuntu-latest
|
||||
# Disable this scheduled job when running on a fork.
|
||||
if: ${{ github.repository == 'RustPython/RustPython' || github.event_name != 'schedule' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
@@ -111,6 +117,8 @@ jobs:
|
||||
benchmark:
|
||||
name: Collect benchmark data
|
||||
runs-on: ubuntu-latest
|
||||
# Disable this scheduled job when running on a fork.
|
||||
if: ${{ github.repository == 'RustPython/RustPython' || github.event_name != 'schedule' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
10
.github/workflows/release.yml
vendored
10
.github/workflows/release.yml
vendored
@@ -21,6 +21,8 @@ env:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.platform.runner }}
|
||||
# Disable this scheduled job when running on a fork.
|
||||
if: ${{ github.repository == 'RustPython/RustPython' || github.event_name != 'schedule' }}
|
||||
strategy:
|
||||
matrix:
|
||||
platform:
|
||||
@@ -88,6 +90,8 @@ jobs:
|
||||
|
||||
build-wasm:
|
||||
runs-on: ubuntu-latest
|
||||
# Disable this scheduled job when running on a fork.
|
||||
if: ${{ github.repository == 'RustPython/RustPython' || github.event_name != 'schedule' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
@@ -108,7 +112,7 @@ jobs:
|
||||
|
||||
- name: install wasm-pack
|
||||
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v5
|
||||
- uses: mwilliamson/setup-wabt-action@v3
|
||||
with: { wabt-version: "1.0.30" }
|
||||
- name: build demo
|
||||
@@ -136,10 +140,12 @@ jobs:
|
||||
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
# Disable this scheduled job when running on a fork.
|
||||
if: ${{ github.repository == 'RustPython/RustPython' || github.event_name != 'schedule' }}
|
||||
needs: [build, build-wasm]
|
||||
steps:
|
||||
- name: Download Binary Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
path: bin
|
||||
pattern: rustpython-*
|
||||
|
||||
538
Cargo.lock
generated
538
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
30
Cargo.toml
30
Cargo.toml
@@ -51,7 +51,7 @@ rustyline = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { workspace = true }
|
||||
pyo3 = { version = "0.24", features = ["auto-initialize"] }
|
||||
pyo3 = { version = "0.26", features = ["auto-initialize"] }
|
||||
|
||||
[[bench]]
|
||||
name = "execution"
|
||||
@@ -163,27 +163,27 @@ ruff_python_ast = { git = "https://github.com/astral-sh/ruff.git", tag = "0.11.0
|
||||
ruff_text_size = { git = "https://github.com/astral-sh/ruff.git", tag = "0.11.0" }
|
||||
ruff_source_file = { git = "https://github.com/astral-sh/ruff.git", tag = "0.11.0" }
|
||||
|
||||
ahash = "0.8.11"
|
||||
ahash = "0.8.12"
|
||||
ascii = "1.1"
|
||||
bitflags = "2.9.1"
|
||||
bitflags = "2.9.4"
|
||||
bstr = "1"
|
||||
cfg-if = "1.0"
|
||||
chrono = "0.4.39"
|
||||
chrono = "0.4.42"
|
||||
constant_time_eq = "0.4"
|
||||
criterion = { version = "0.5", features = ["html_reports"] }
|
||||
criterion = { version = "0.7", features = ["html_reports"] }
|
||||
crossbeam-utils = "0.8.21"
|
||||
flame = "0.2.2"
|
||||
getrandom = { version = "0.3", features = ["std"] }
|
||||
glob = "0.3"
|
||||
hex = "0.4.3"
|
||||
indexmap = { version = "2.10.0", features = ["std"] }
|
||||
indexmap = { version = "2.11.3", features = ["std"] }
|
||||
insta = "1.42"
|
||||
itertools = "0.14.0"
|
||||
is-macro = "0.3.7"
|
||||
junction = "1.2.0"
|
||||
junction = "1.3.0"
|
||||
libc = "0.2.169"
|
||||
libffi = "4.1"
|
||||
log = "0.4.27"
|
||||
log = "0.4.28"
|
||||
nix = { version = "0.30", features = ["fs", "user", "process", "term", "time", "signal", "ioctl", "socket", "sched", "zerocopy", "dir", "hostname", "net", "poll"] }
|
||||
malachite-bigint = "0.6"
|
||||
malachite-q = "0.6"
|
||||
@@ -204,9 +204,9 @@ radium = "1.1.1"
|
||||
rand = "0.9"
|
||||
rand_core = { version = "0.9", features = ["os_rng"] }
|
||||
rustix = { version = "1.0", features = ["event"] }
|
||||
rustyline = "17.0.0"
|
||||
serde = { version = "1.0.133", default-features = false }
|
||||
schannel = "0.1.27"
|
||||
rustyline = "17.0.1"
|
||||
serde = { version = "1.0.225", default-features = false }
|
||||
schannel = "0.1.28"
|
||||
scoped-tls = "1"
|
||||
scopeguard = "1"
|
||||
static_assertions = "1.1"
|
||||
@@ -214,16 +214,16 @@ strum = "0.27"
|
||||
strum_macros = "0.27"
|
||||
syn = "2"
|
||||
thiserror = "2.0"
|
||||
thread_local = "1.1.8"
|
||||
unicode-casing = "0.1.0"
|
||||
thread_local = "1.1.9"
|
||||
unicode-casing = "0.1.1"
|
||||
unic-char-property = "0.9.0"
|
||||
unic-normal = "0.9.0"
|
||||
unic-ucd-age = "0.9.0"
|
||||
unic-ucd-bidi = "0.9.0"
|
||||
unic-ucd-category = "0.9.0"
|
||||
unic-ucd-ident = "0.9.0"
|
||||
unicode_names2 = "1.3.0"
|
||||
unicode-bidi-mirroring = "0.2"
|
||||
unicode_names2 = "2.0.0"
|
||||
unicode-bidi-mirroring = "0.4"
|
||||
widestring = "1.2.0"
|
||||
windows-sys = "0.59.0"
|
||||
wasm-bindgen = "0.2.100"
|
||||
|
||||
343
Lib/_opcode_metadata.py
vendored
Normal file
343
Lib/_opcode_metadata.py
vendored
Normal file
@@ -0,0 +1,343 @@
|
||||
# This file is generated by Tools/cases_generator/py_metadata_generator.py
|
||||
# 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_INPLACE_ADD_UNICODE",
|
||||
],
|
||||
"BINARY_SUBSCR": [
|
||||
"BINARY_SUBSCR_DICT",
|
||||
"BINARY_SUBSCR_GETITEM",
|
||||
"BINARY_SUBSCR_LIST_INT",
|
||||
"BINARY_SUBSCR_STR_INT",
|
||||
"BINARY_SUBSCR_TUPLE_INT",
|
||||
],
|
||||
"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_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",
|
||||
],
|
||||
"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",
|
||||
],
|
||||
}
|
||||
|
||||
_specialized_opmap = {
|
||||
'BINARY_OP_ADD_FLOAT': 150,
|
||||
'BINARY_OP_ADD_INT': 151,
|
||||
'BINARY_OP_ADD_UNICODE': 152,
|
||||
'BINARY_OP_INPLACE_ADD_UNICODE': 3,
|
||||
'BINARY_OP_MULTIPLY_FLOAT': 153,
|
||||
'BINARY_OP_MULTIPLY_INT': 154,
|
||||
'BINARY_OP_SUBTRACT_FLOAT': 155,
|
||||
'BINARY_OP_SUBTRACT_INT': 156,
|
||||
'BINARY_SUBSCR_DICT': 157,
|
||||
'BINARY_SUBSCR_GETITEM': 158,
|
||||
'BINARY_SUBSCR_LIST_INT': 159,
|
||||
'BINARY_SUBSCR_STR_INT': 160,
|
||||
'BINARY_SUBSCR_TUPLE_INT': 161,
|
||||
'CALL_ALLOC_AND_ENTER_INIT': 162,
|
||||
'CALL_BOUND_METHOD_EXACT_ARGS': 163,
|
||||
'CALL_BOUND_METHOD_GENERAL': 164,
|
||||
'CALL_BUILTIN_CLASS': 165,
|
||||
'CALL_BUILTIN_FAST': 166,
|
||||
'CALL_BUILTIN_FAST_WITH_KEYWORDS': 167,
|
||||
'CALL_BUILTIN_O': 168,
|
||||
'CALL_ISINSTANCE': 169,
|
||||
'CALL_LEN': 170,
|
||||
'CALL_LIST_APPEND': 171,
|
||||
'CALL_METHOD_DESCRIPTOR_FAST': 172,
|
||||
'CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS': 173,
|
||||
'CALL_METHOD_DESCRIPTOR_NOARGS': 174,
|
||||
'CALL_METHOD_DESCRIPTOR_O': 175,
|
||||
'CALL_NON_PY_GENERAL': 176,
|
||||
'CALL_PY_EXACT_ARGS': 177,
|
||||
'CALL_PY_GENERAL': 178,
|
||||
'CALL_STR_1': 179,
|
||||
'CALL_TUPLE_1': 180,
|
||||
'CALL_TYPE_1': 181,
|
||||
'COMPARE_OP_FLOAT': 182,
|
||||
'COMPARE_OP_INT': 183,
|
||||
'COMPARE_OP_STR': 184,
|
||||
'CONTAINS_OP_DICT': 185,
|
||||
'CONTAINS_OP_SET': 186,
|
||||
'FOR_ITER_GEN': 187,
|
||||
'FOR_ITER_LIST': 188,
|
||||
'FOR_ITER_RANGE': 189,
|
||||
'FOR_ITER_TUPLE': 190,
|
||||
'LOAD_ATTR_CLASS': 191,
|
||||
'LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN': 192,
|
||||
'LOAD_ATTR_INSTANCE_VALUE': 193,
|
||||
'LOAD_ATTR_METHOD_LAZY_DICT': 194,
|
||||
'LOAD_ATTR_METHOD_NO_DICT': 195,
|
||||
'LOAD_ATTR_METHOD_WITH_VALUES': 196,
|
||||
'LOAD_ATTR_MODULE': 197,
|
||||
'LOAD_ATTR_NONDESCRIPTOR_NO_DICT': 198,
|
||||
'LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES': 199,
|
||||
'LOAD_ATTR_PROPERTY': 200,
|
||||
'LOAD_ATTR_SLOT': 201,
|
||||
'LOAD_ATTR_WITH_HINT': 202,
|
||||
'LOAD_GLOBAL_BUILTIN': 203,
|
||||
'LOAD_GLOBAL_MODULE': 204,
|
||||
'LOAD_SUPER_ATTR_ATTR': 205,
|
||||
'LOAD_SUPER_ATTR_METHOD': 206,
|
||||
'RESUME_CHECK': 207,
|
||||
'SEND_GEN': 208,
|
||||
'STORE_ATTR_INSTANCE_VALUE': 209,
|
||||
'STORE_ATTR_SLOT': 210,
|
||||
'STORE_ATTR_WITH_HINT': 211,
|
||||
'STORE_SUBSCR_DICT': 212,
|
||||
'STORE_SUBSCR_LIST_INT': 213,
|
||||
'TO_BOOL_ALWAYS_TRUE': 214,
|
||||
'TO_BOOL_BOOL': 215,
|
||||
'TO_BOOL_INT': 216,
|
||||
'TO_BOOL_LIST': 217,
|
||||
'TO_BOOL_NONE': 218,
|
||||
'TO_BOOL_STR': 219,
|
||||
'UNPACK_SEQUENCE_LIST': 220,
|
||||
'UNPACK_SEQUENCE_TUPLE': 221,
|
||||
'UNPACK_SEQUENCE_TWO_TUPLE': 222,
|
||||
}
|
||||
|
||||
opmap = {
|
||||
'CACHE': 0,
|
||||
'RESERVED': 17,
|
||||
'RESUME': 149,
|
||||
'INSTRUMENTED_LINE': 254,
|
||||
'BEFORE_ASYNC_WITH': 1,
|
||||
'BEFORE_WITH': 2,
|
||||
'BINARY_SLICE': 4,
|
||||
'BINARY_SUBSCR': 5,
|
||||
'CHECK_EG_MATCH': 6,
|
||||
'CHECK_EXC_MATCH': 7,
|
||||
'CLEANUP_THROW': 8,
|
||||
'DELETE_SUBSCR': 9,
|
||||
'END_ASYNC_FOR': 10,
|
||||
'END_FOR': 11,
|
||||
'END_SEND': 12,
|
||||
'EXIT_INIT_CHECK': 13,
|
||||
'FORMAT_SIMPLE': 14,
|
||||
'FORMAT_WITH_SPEC': 15,
|
||||
'GET_AITER': 16,
|
||||
'GET_ANEXT': 18,
|
||||
'GET_ITER': 19,
|
||||
'GET_LEN': 20,
|
||||
'GET_YIELD_FROM_ITER': 21,
|
||||
'INTERPRETER_EXIT': 22,
|
||||
'LOAD_ASSERTION_ERROR': 23,
|
||||
'LOAD_BUILD_CLASS': 24,
|
||||
'LOAD_LOCALS': 25,
|
||||
'MAKE_FUNCTION': 26,
|
||||
'MATCH_KEYS': 27,
|
||||
'MATCH_MAPPING': 28,
|
||||
'MATCH_SEQUENCE': 29,
|
||||
'NOP': 30,
|
||||
'POP_EXCEPT': 31,
|
||||
'POP_TOP': 32,
|
||||
'PUSH_EXC_INFO': 33,
|
||||
'PUSH_NULL': 34,
|
||||
'RETURN_GENERATOR': 35,
|
||||
'RETURN_VALUE': 36,
|
||||
'SETUP_ANNOTATIONS': 37,
|
||||
'STORE_SLICE': 38,
|
||||
'STORE_SUBSCR': 39,
|
||||
'TO_BOOL': 40,
|
||||
'UNARY_INVERT': 41,
|
||||
'UNARY_NEGATIVE': 42,
|
||||
'UNARY_NOT': 43,
|
||||
'WITH_EXCEPT_START': 44,
|
||||
'BINARY_OP': 45,
|
||||
'BUILD_CONST_KEY_MAP': 46,
|
||||
'BUILD_LIST': 47,
|
||||
'BUILD_MAP': 48,
|
||||
'BUILD_SET': 49,
|
||||
'BUILD_SLICE': 50,
|
||||
'BUILD_STRING': 51,
|
||||
'BUILD_TUPLE': 52,
|
||||
'CALL': 53,
|
||||
'CALL_FUNCTION_EX': 54,
|
||||
'CALL_INTRINSIC_1': 55,
|
||||
'CALL_INTRINSIC_2': 56,
|
||||
'CALL_KW': 57,
|
||||
'COMPARE_OP': 58,
|
||||
'CONTAINS_OP': 59,
|
||||
'CONVERT_VALUE': 60,
|
||||
'COPY': 61,
|
||||
'COPY_FREE_VARS': 62,
|
||||
'DELETE_ATTR': 63,
|
||||
'DELETE_DEREF': 64,
|
||||
'DELETE_FAST': 65,
|
||||
'DELETE_GLOBAL': 66,
|
||||
'DELETE_NAME': 67,
|
||||
'DICT_MERGE': 68,
|
||||
'DICT_UPDATE': 69,
|
||||
'ENTER_EXECUTOR': 70,
|
||||
'EXTENDED_ARG': 71,
|
||||
'FOR_ITER': 72,
|
||||
'GET_AWAITABLE': 73,
|
||||
'IMPORT_FROM': 74,
|
||||
'IMPORT_NAME': 75,
|
||||
'IS_OP': 76,
|
||||
'JUMP_BACKWARD': 77,
|
||||
'JUMP_BACKWARD_NO_INTERRUPT': 78,
|
||||
'JUMP_FORWARD': 79,
|
||||
'LIST_APPEND': 80,
|
||||
'LIST_EXTEND': 81,
|
||||
'LOAD_ATTR': 82,
|
||||
'LOAD_CONST': 83,
|
||||
'LOAD_DEREF': 84,
|
||||
'LOAD_FAST': 85,
|
||||
'LOAD_FAST_AND_CLEAR': 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_SUPER_ATTR': 93,
|
||||
'MAKE_CELL': 94,
|
||||
'MAP_ADD': 95,
|
||||
'MATCH_CLASS': 96,
|
||||
'POP_JUMP_IF_FALSE': 97,
|
||||
'POP_JUMP_IF_NONE': 98,
|
||||
'POP_JUMP_IF_NOT_NONE': 99,
|
||||
'POP_JUMP_IF_TRUE': 100,
|
||||
'RAISE_VARARGS': 101,
|
||||
'RERAISE': 102,
|
||||
'RETURN_CONST': 103,
|
||||
'SEND': 104,
|
||||
'SET_ADD': 105,
|
||||
'SET_FUNCTION_ATTRIBUTE': 106,
|
||||
'SET_UPDATE': 107,
|
||||
'STORE_ATTR': 108,
|
||||
'STORE_DEREF': 109,
|
||||
'STORE_FAST': 110,
|
||||
'STORE_FAST_LOAD_FAST': 111,
|
||||
'STORE_FAST_STORE_FAST': 112,
|
||||
'STORE_GLOBAL': 113,
|
||||
'STORE_NAME': 114,
|
||||
'SWAP': 115,
|
||||
'UNPACK_EX': 116,
|
||||
'UNPACK_SEQUENCE': 117,
|
||||
'YIELD_VALUE': 118,
|
||||
'INSTRUMENTED_RESUME': 236,
|
||||
'INSTRUMENTED_END_FOR': 237,
|
||||
'INSTRUMENTED_END_SEND': 238,
|
||||
'INSTRUMENTED_RETURN_VALUE': 239,
|
||||
'INSTRUMENTED_RETURN_CONST': 240,
|
||||
'INSTRUMENTED_YIELD_VALUE': 241,
|
||||
'INSTRUMENTED_LOAD_SUPER_ATTR': 242,
|
||||
'INSTRUMENTED_FOR_ITER': 243,
|
||||
'INSTRUMENTED_CALL': 244,
|
||||
'INSTRUMENTED_CALL_KW': 245,
|
||||
'INSTRUMENTED_CALL_FUNCTION_EX': 246,
|
||||
'INSTRUMENTED_INSTRUCTION': 247,
|
||||
'INSTRUMENTED_JUMP_FORWARD': 248,
|
||||
'INSTRUMENTED_JUMP_BACKWARD': 249,
|
||||
'INSTRUMENTED_POP_JUMP_IF_TRUE': 250,
|
||||
'INSTRUMENTED_POP_JUMP_IF_FALSE': 251,
|
||||
'INSTRUMENTED_POP_JUMP_IF_NONE': 252,
|
||||
'INSTRUMENTED_POP_JUMP_IF_NOT_NONE': 253,
|
||||
'JUMP': 256,
|
||||
'JUMP_NO_INTERRUPT': 257,
|
||||
'LOAD_CLOSURE': 258,
|
||||
'LOAD_METHOD': 259,
|
||||
'LOAD_SUPER_METHOD': 260,
|
||||
'LOAD_ZERO_SUPER_ATTR': 261,
|
||||
'LOAD_ZERO_SUPER_METHOD': 262,
|
||||
'POP_BLOCK': 263,
|
||||
'SETUP_CLEANUP': 264,
|
||||
'SETUP_FINALLY': 265,
|
||||
'SETUP_WITH': 266,
|
||||
'STORE_FAST_MAYBE_NULL': 267,
|
||||
}
|
||||
|
||||
HAVE_ARGUMENT = 44
|
||||
MIN_INSTRUMENTED_OPCODE = 236
|
||||
1
Lib/encodings/aliases.py
vendored
1
Lib/encodings/aliases.py
vendored
@@ -209,6 +209,7 @@ aliases = {
|
||||
'ms932' : 'cp932',
|
||||
'mskanji' : 'cp932',
|
||||
'ms_kanji' : 'cp932',
|
||||
'windows_31j' : 'cp932',
|
||||
|
||||
# cp949 codec
|
||||
'949' : 'cp949',
|
||||
|
||||
166
Lib/encodings/idna.py
vendored
166
Lib/encodings/idna.py
vendored
@@ -11,7 +11,7 @@ ace_prefix = b"xn--"
|
||||
sace_prefix = "xn--"
|
||||
|
||||
# This assumes query strings, so AllowUnassigned is true
|
||||
def nameprep(label):
|
||||
def nameprep(label): # type: (str) -> str
|
||||
# Map
|
||||
newlabel = []
|
||||
for c in label:
|
||||
@@ -25,7 +25,7 @@ def nameprep(label):
|
||||
label = unicodedata.normalize("NFKC", label)
|
||||
|
||||
# Prohibit
|
||||
for c in label:
|
||||
for i, c in enumerate(label):
|
||||
if stringprep.in_table_c12(c) or \
|
||||
stringprep.in_table_c22(c) or \
|
||||
stringprep.in_table_c3(c) or \
|
||||
@@ -35,7 +35,7 @@ def nameprep(label):
|
||||
stringprep.in_table_c7(c) or \
|
||||
stringprep.in_table_c8(c) or \
|
||||
stringprep.in_table_c9(c):
|
||||
raise UnicodeError("Invalid character %r" % c)
|
||||
raise UnicodeEncodeError("idna", label, i, i+1, f"Invalid character {c!r}")
|
||||
|
||||
# Check bidi
|
||||
RandAL = [stringprep.in_table_d1(x) for x in label]
|
||||
@@ -46,29 +46,38 @@ def nameprep(label):
|
||||
# This is table C.8, which was already checked
|
||||
# 2) If a string contains any RandALCat character, the string
|
||||
# MUST NOT contain any LCat character.
|
||||
if any(stringprep.in_table_d2(x) for x in label):
|
||||
raise UnicodeError("Violation of BIDI requirement 2")
|
||||
for i, x in enumerate(label):
|
||||
if stringprep.in_table_d2(x):
|
||||
raise UnicodeEncodeError("idna", label, i, i+1,
|
||||
"Violation of BIDI requirement 2")
|
||||
# 3) If a string contains any RandALCat character, a
|
||||
# RandALCat character MUST be the first character of the
|
||||
# string, and a RandALCat character MUST be the last
|
||||
# character of the string.
|
||||
if not RandAL[0] or not RandAL[-1]:
|
||||
raise UnicodeError("Violation of BIDI requirement 3")
|
||||
if not RandAL[0]:
|
||||
raise UnicodeEncodeError("idna", label, 0, 1,
|
||||
"Violation of BIDI requirement 3")
|
||||
if not RandAL[-1]:
|
||||
raise UnicodeEncodeError("idna", label, len(label)-1, len(label),
|
||||
"Violation of BIDI requirement 3")
|
||||
|
||||
return label
|
||||
|
||||
def ToASCII(label):
|
||||
def ToASCII(label): # type: (str) -> bytes
|
||||
try:
|
||||
# Step 1: try ASCII
|
||||
label = label.encode("ascii")
|
||||
except UnicodeError:
|
||||
label_ascii = label.encode("ascii")
|
||||
except UnicodeEncodeError:
|
||||
pass
|
||||
else:
|
||||
# Skip to step 3: UseSTD3ASCIIRules is false, so
|
||||
# Skip to step 8.
|
||||
if 0 < len(label) < 64:
|
||||
return label
|
||||
raise UnicodeError("label empty or too long")
|
||||
if 0 < len(label_ascii) < 64:
|
||||
return label_ascii
|
||||
if len(label) == 0:
|
||||
raise UnicodeEncodeError("idna", label, 0, 1, "label empty")
|
||||
else:
|
||||
raise UnicodeEncodeError("idna", label, 0, len(label), "label too long")
|
||||
|
||||
# Step 2: nameprep
|
||||
label = nameprep(label)
|
||||
@@ -76,29 +85,34 @@ def ToASCII(label):
|
||||
# Step 3: UseSTD3ASCIIRules is false
|
||||
# Step 4: try ASCII
|
||||
try:
|
||||
label = label.encode("ascii")
|
||||
except UnicodeError:
|
||||
label_ascii = label.encode("ascii")
|
||||
except UnicodeEncodeError:
|
||||
pass
|
||||
else:
|
||||
# Skip to step 8.
|
||||
if 0 < len(label) < 64:
|
||||
return label
|
||||
raise UnicodeError("label empty or too long")
|
||||
return label_ascii
|
||||
if len(label) == 0:
|
||||
raise UnicodeEncodeError("idna", label, 0, 1, "label empty")
|
||||
else:
|
||||
raise UnicodeEncodeError("idna", label, 0, len(label), "label too long")
|
||||
|
||||
# Step 5: Check ACE prefix
|
||||
if label.startswith(sace_prefix):
|
||||
raise UnicodeError("Label starts with ACE prefix")
|
||||
if label.lower().startswith(sace_prefix):
|
||||
raise UnicodeEncodeError(
|
||||
"idna", label, 0, len(sace_prefix), "Label starts with ACE prefix")
|
||||
|
||||
# Step 6: Encode with PUNYCODE
|
||||
label = label.encode("punycode")
|
||||
label_ascii = label.encode("punycode")
|
||||
|
||||
# Step 7: Prepend ACE prefix
|
||||
label = ace_prefix + label
|
||||
label_ascii = ace_prefix + label_ascii
|
||||
|
||||
# Step 8: Check size
|
||||
if 0 < len(label) < 64:
|
||||
return label
|
||||
raise UnicodeError("label empty or too long")
|
||||
# do not check for empty as we prepend ace_prefix.
|
||||
if len(label_ascii) < 64:
|
||||
return label_ascii
|
||||
raise UnicodeEncodeError("idna", label, 0, len(label), "label too long")
|
||||
|
||||
def ToUnicode(label):
|
||||
if len(label) > 1024:
|
||||
@@ -110,7 +124,9 @@ def ToUnicode(label):
|
||||
# per https://www.rfc-editor.org/rfc/rfc3454#section-3.1 while still
|
||||
# preventing us from wasting time decoding a big thing that'll just
|
||||
# hit the actual <= 63 length limit in Step 6.
|
||||
raise UnicodeError("label way too long")
|
||||
if isinstance(label, str):
|
||||
label = label.encode("utf-8", errors="backslashreplace")
|
||||
raise UnicodeDecodeError("idna", label, 0, len(label), "label way too long")
|
||||
# Step 1: Check for ASCII
|
||||
if isinstance(label, bytes):
|
||||
pure_ascii = True
|
||||
@@ -118,25 +134,32 @@ def ToUnicode(label):
|
||||
try:
|
||||
label = label.encode("ascii")
|
||||
pure_ascii = True
|
||||
except UnicodeError:
|
||||
except UnicodeEncodeError:
|
||||
pure_ascii = False
|
||||
if not pure_ascii:
|
||||
assert isinstance(label, str)
|
||||
# Step 2: Perform nameprep
|
||||
label = nameprep(label)
|
||||
# It doesn't say this, but apparently, it should be ASCII now
|
||||
try:
|
||||
label = label.encode("ascii")
|
||||
except UnicodeError:
|
||||
raise UnicodeError("Invalid character in IDN label")
|
||||
except UnicodeEncodeError as exc:
|
||||
raise UnicodeEncodeError("idna", label, exc.start, exc.end,
|
||||
"Invalid character in IDN label")
|
||||
# Step 3: Check for ACE prefix
|
||||
if not label.startswith(ace_prefix):
|
||||
assert isinstance(label, bytes)
|
||||
if not label.lower().startswith(ace_prefix):
|
||||
return str(label, "ascii")
|
||||
|
||||
# Step 4: Remove ACE prefix
|
||||
label1 = label[len(ace_prefix):]
|
||||
|
||||
# Step 5: Decode using PUNYCODE
|
||||
result = label1.decode("punycode")
|
||||
try:
|
||||
result = label1.decode("punycode")
|
||||
except UnicodeDecodeError as exc:
|
||||
offset = len(ace_prefix)
|
||||
raise UnicodeDecodeError("idna", label, offset+exc.start, offset+exc.end, exc.reason)
|
||||
|
||||
# Step 6: Apply ToASCII
|
||||
label2 = ToASCII(result)
|
||||
@@ -144,7 +167,8 @@ def ToUnicode(label):
|
||||
# Step 7: Compare the result of step 6 with the one of step 3
|
||||
# label2 will already be in lower case.
|
||||
if str(label, "ascii").lower() != str(label2, "ascii"):
|
||||
raise UnicodeError("IDNA does not round-trip", label, label2)
|
||||
raise UnicodeDecodeError("idna", label, 0, len(label),
|
||||
f"IDNA does not round-trip, '{label!r}' != '{label2!r}'")
|
||||
|
||||
# Step 8: return the result of step 5
|
||||
return result
|
||||
@@ -156,7 +180,7 @@ class Codec(codecs.Codec):
|
||||
|
||||
if errors != 'strict':
|
||||
# IDNA is quite clear that implementations must be strict
|
||||
raise UnicodeError("unsupported error handling "+errors)
|
||||
raise UnicodeError(f"Unsupported error handling: {errors}")
|
||||
|
||||
if not input:
|
||||
return b'', 0
|
||||
@@ -168,11 +192,16 @@ class Codec(codecs.Codec):
|
||||
else:
|
||||
# ASCII name: fast path
|
||||
labels = result.split(b'.')
|
||||
for label in labels[:-1]:
|
||||
if not (0 < len(label) < 64):
|
||||
raise UnicodeError("label empty or too long")
|
||||
if len(labels[-1]) >= 64:
|
||||
raise UnicodeError("label too long")
|
||||
for i, label in enumerate(labels[:-1]):
|
||||
if len(label) == 0:
|
||||
offset = sum(len(l) for l in labels[:i]) + i
|
||||
raise UnicodeEncodeError("idna", input, offset, offset+1,
|
||||
"label empty")
|
||||
for i, label in enumerate(labels):
|
||||
if len(label) >= 64:
|
||||
offset = sum(len(l) for l in labels[:i]) + i
|
||||
raise UnicodeEncodeError("idna", input, offset, offset+len(label),
|
||||
"label too long")
|
||||
return result, len(input)
|
||||
|
||||
result = bytearray()
|
||||
@@ -182,17 +211,27 @@ class Codec(codecs.Codec):
|
||||
del labels[-1]
|
||||
else:
|
||||
trailing_dot = b''
|
||||
for label in labels:
|
||||
for i, label in enumerate(labels):
|
||||
if result:
|
||||
# Join with U+002E
|
||||
result.extend(b'.')
|
||||
result.extend(ToASCII(label))
|
||||
try:
|
||||
result.extend(ToASCII(label))
|
||||
except (UnicodeEncodeError, UnicodeDecodeError) as exc:
|
||||
offset = sum(len(l) for l in labels[:i]) + i
|
||||
raise UnicodeEncodeError(
|
||||
"idna",
|
||||
input,
|
||||
offset + exc.start,
|
||||
offset + exc.end,
|
||||
exc.reason,
|
||||
)
|
||||
return bytes(result+trailing_dot), len(input)
|
||||
|
||||
def decode(self, input, errors='strict'):
|
||||
|
||||
if errors != 'strict':
|
||||
raise UnicodeError("Unsupported error handling "+errors)
|
||||
raise UnicodeError(f"Unsupported error handling: {errors}")
|
||||
|
||||
if not input:
|
||||
return "", 0
|
||||
@@ -202,7 +241,7 @@ class Codec(codecs.Codec):
|
||||
# XXX obviously wrong, see #3232
|
||||
input = bytes(input)
|
||||
|
||||
if ace_prefix not in input:
|
||||
if ace_prefix not in input.lower():
|
||||
# Fast path
|
||||
try:
|
||||
return input.decode('ascii'), len(input)
|
||||
@@ -218,8 +257,15 @@ class Codec(codecs.Codec):
|
||||
trailing_dot = ''
|
||||
|
||||
result = []
|
||||
for label in labels:
|
||||
result.append(ToUnicode(label))
|
||||
for i, label in enumerate(labels):
|
||||
try:
|
||||
u_label = ToUnicode(label)
|
||||
except (UnicodeEncodeError, UnicodeDecodeError) as exc:
|
||||
offset = sum(len(x) for x in labels[:i]) + len(labels[:i])
|
||||
raise UnicodeDecodeError(
|
||||
"idna", input, offset+exc.start, offset+exc.end, exc.reason)
|
||||
else:
|
||||
result.append(u_label)
|
||||
|
||||
return ".".join(result)+trailing_dot, len(input)
|
||||
|
||||
@@ -227,7 +273,7 @@ class IncrementalEncoder(codecs.BufferedIncrementalEncoder):
|
||||
def _buffer_encode(self, input, errors, final):
|
||||
if errors != 'strict':
|
||||
# IDNA is quite clear that implementations must be strict
|
||||
raise UnicodeError("unsupported error handling "+errors)
|
||||
raise UnicodeError(f"Unsupported error handling: {errors}")
|
||||
|
||||
if not input:
|
||||
return (b'', 0)
|
||||
@@ -251,7 +297,16 @@ class IncrementalEncoder(codecs.BufferedIncrementalEncoder):
|
||||
# Join with U+002E
|
||||
result.extend(b'.')
|
||||
size += 1
|
||||
result.extend(ToASCII(label))
|
||||
try:
|
||||
result.extend(ToASCII(label))
|
||||
except (UnicodeEncodeError, UnicodeDecodeError) as exc:
|
||||
raise UnicodeEncodeError(
|
||||
"idna",
|
||||
input,
|
||||
size + exc.start,
|
||||
size + exc.end,
|
||||
exc.reason,
|
||||
)
|
||||
size += len(label)
|
||||
|
||||
result += trailing_dot
|
||||
@@ -261,7 +316,7 @@ class IncrementalEncoder(codecs.BufferedIncrementalEncoder):
|
||||
class IncrementalDecoder(codecs.BufferedIncrementalDecoder):
|
||||
def _buffer_decode(self, input, errors, final):
|
||||
if errors != 'strict':
|
||||
raise UnicodeError("Unsupported error handling "+errors)
|
||||
raise UnicodeError(f"Unsupported error handling: {errors}")
|
||||
|
||||
if not input:
|
||||
return ("", 0)
|
||||
@@ -271,7 +326,11 @@ class IncrementalDecoder(codecs.BufferedIncrementalDecoder):
|
||||
labels = dots.split(input)
|
||||
else:
|
||||
# Must be ASCII string
|
||||
input = str(input, "ascii")
|
||||
try:
|
||||
input = str(input, "ascii")
|
||||
except (UnicodeEncodeError, UnicodeDecodeError) as exc:
|
||||
raise UnicodeDecodeError("idna", input,
|
||||
exc.start, exc.end, exc.reason)
|
||||
labels = input.split(".")
|
||||
|
||||
trailing_dot = ''
|
||||
@@ -288,7 +347,18 @@ class IncrementalDecoder(codecs.BufferedIncrementalDecoder):
|
||||
result = []
|
||||
size = 0
|
||||
for label in labels:
|
||||
result.append(ToUnicode(label))
|
||||
try:
|
||||
u_label = ToUnicode(label)
|
||||
except (UnicodeEncodeError, UnicodeDecodeError) as exc:
|
||||
raise UnicodeDecodeError(
|
||||
"idna",
|
||||
input.encode("ascii", errors="backslashreplace"),
|
||||
size + exc.start,
|
||||
size + exc.end,
|
||||
exc.reason,
|
||||
)
|
||||
else:
|
||||
result.append(u_label)
|
||||
if size:
|
||||
size += 1
|
||||
size += len(label)
|
||||
|
||||
2
Lib/encodings/palmos.py
vendored
2
Lib/encodings/palmos.py
vendored
@@ -201,7 +201,7 @@ decoding_table = (
|
||||
'\u02dc' # 0x98 -> SMALL TILDE
|
||||
'\u2122' # 0x99 -> TRADE MARK SIGN
|
||||
'\u0161' # 0x9A -> LATIN SMALL LETTER S WITH CARON
|
||||
'\x9b' # 0x9B -> <control>
|
||||
'\u203a' # 0x9B -> SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
|
||||
'\u0153' # 0x9C -> LATIN SMALL LIGATURE OE
|
||||
'\x9d' # 0x9D -> <control>
|
||||
'\x9e' # 0x9E -> <control>
|
||||
|
||||
42
Lib/encodings/punycode.py
vendored
42
Lib/encodings/punycode.py
vendored
@@ -1,4 +1,4 @@
|
||||
""" Codec for the Punicode encoding, as specified in RFC 3492
|
||||
""" Codec for the Punycode encoding, as specified in RFC 3492
|
||||
|
||||
Written by Martin v. Löwis.
|
||||
"""
|
||||
@@ -131,10 +131,11 @@ def decode_generalized_number(extended, extpos, bias, errors):
|
||||
j = 0
|
||||
while 1:
|
||||
try:
|
||||
char = ord(extended[extpos])
|
||||
char = extended[extpos]
|
||||
except IndexError:
|
||||
if errors == "strict":
|
||||
raise UnicodeError("incomplete punicode string")
|
||||
raise UnicodeDecodeError("punycode", extended, extpos, extpos+1,
|
||||
"incomplete punycode string")
|
||||
return extpos + 1, None
|
||||
extpos += 1
|
||||
if 0x41 <= char <= 0x5A: # A-Z
|
||||
@@ -142,8 +143,8 @@ def decode_generalized_number(extended, extpos, bias, errors):
|
||||
elif 0x30 <= char <= 0x39:
|
||||
digit = char - 22 # 0x30-26
|
||||
elif errors == "strict":
|
||||
raise UnicodeError("Invalid extended code point '%s'"
|
||||
% extended[extpos-1])
|
||||
raise UnicodeDecodeError("punycode", extended, extpos-1, extpos,
|
||||
f"Invalid extended code point '{extended[extpos-1]}'")
|
||||
else:
|
||||
return extpos, None
|
||||
t = T(j, bias)
|
||||
@@ -155,11 +156,14 @@ def decode_generalized_number(extended, extpos, bias, errors):
|
||||
|
||||
|
||||
def insertion_sort(base, extended, errors):
|
||||
"""3.2 Insertion unsort coding"""
|
||||
"""3.2 Insertion sort coding"""
|
||||
# This function raises UnicodeDecodeError with position in the extended.
|
||||
# Caller should add the offset.
|
||||
char = 0x80
|
||||
pos = -1
|
||||
bias = 72
|
||||
extpos = 0
|
||||
|
||||
while extpos < len(extended):
|
||||
newpos, delta = decode_generalized_number(extended, extpos,
|
||||
bias, errors)
|
||||
@@ -171,7 +175,9 @@ def insertion_sort(base, extended, errors):
|
||||
char += pos // (len(base) + 1)
|
||||
if char > 0x10FFFF:
|
||||
if errors == "strict":
|
||||
raise UnicodeError("Invalid character U+%x" % char)
|
||||
raise UnicodeDecodeError(
|
||||
"punycode", extended, pos-1, pos,
|
||||
f"Invalid character U+{char:x}")
|
||||
char = ord('?')
|
||||
pos = pos % (len(base) + 1)
|
||||
base = base[:pos] + chr(char) + base[pos:]
|
||||
@@ -187,11 +193,21 @@ def punycode_decode(text, errors):
|
||||
pos = text.rfind(b"-")
|
||||
if pos == -1:
|
||||
base = ""
|
||||
extended = str(text, "ascii").upper()
|
||||
extended = text.upper()
|
||||
else:
|
||||
base = str(text[:pos], "ascii", errors)
|
||||
extended = str(text[pos+1:], "ascii").upper()
|
||||
return insertion_sort(base, extended, errors)
|
||||
try:
|
||||
base = str(text[:pos], "ascii", errors)
|
||||
except UnicodeDecodeError as exc:
|
||||
raise UnicodeDecodeError("ascii", text, exc.start, exc.end,
|
||||
exc.reason) from None
|
||||
extended = text[pos+1:].upper()
|
||||
try:
|
||||
return insertion_sort(base, extended, errors)
|
||||
except UnicodeDecodeError as exc:
|
||||
offset = pos + 1
|
||||
raise UnicodeDecodeError("punycode", text,
|
||||
offset+exc.start, offset+exc.end,
|
||||
exc.reason) from None
|
||||
|
||||
### Codec APIs
|
||||
|
||||
@@ -203,7 +219,7 @@ class Codec(codecs.Codec):
|
||||
|
||||
def decode(self, input, errors='strict'):
|
||||
if errors not in ('strict', 'replace', 'ignore'):
|
||||
raise UnicodeError("Unsupported error handling "+errors)
|
||||
raise UnicodeError(f"Unsupported error handling: {errors}")
|
||||
res = punycode_decode(input, errors)
|
||||
return res, len(input)
|
||||
|
||||
@@ -214,7 +230,7 @@ class IncrementalEncoder(codecs.IncrementalEncoder):
|
||||
class IncrementalDecoder(codecs.IncrementalDecoder):
|
||||
def decode(self, input, final=False):
|
||||
if self.errors not in ('strict', 'replace', 'ignore'):
|
||||
raise UnicodeError("Unsupported error handling "+self.errors)
|
||||
raise UnicodeError(f"Unsupported error handling: {self.errors}")
|
||||
return punycode_decode(input, self.errors)
|
||||
|
||||
class StreamWriter(Codec,codecs.StreamWriter):
|
||||
|
||||
2
Lib/encodings/undefined.py
vendored
2
Lib/encodings/undefined.py
vendored
@@ -1,6 +1,6 @@
|
||||
""" Python 'undefined' Codec
|
||||
|
||||
This codec will always raise a ValueError exception when being
|
||||
This codec will always raise a UnicodeError exception when being
|
||||
used. It is intended for use by the site.py file to switch off
|
||||
automatic string to Unicode coercion.
|
||||
|
||||
|
||||
4
Lib/encodings/utf_16.py
vendored
4
Lib/encodings/utf_16.py
vendored
@@ -64,7 +64,7 @@ class IncrementalDecoder(codecs.BufferedIncrementalDecoder):
|
||||
elif byteorder == 1:
|
||||
self.decoder = codecs.utf_16_be_decode
|
||||
elif consumed >= 2:
|
||||
raise UnicodeError("UTF-16 stream does not start with BOM")
|
||||
raise UnicodeDecodeError("utf-16", input, 0, 2, "Stream does not start with BOM")
|
||||
return (output, consumed)
|
||||
return self.decoder(input, self.errors, final)
|
||||
|
||||
@@ -138,7 +138,7 @@ class StreamReader(codecs.StreamReader):
|
||||
elif byteorder == 1:
|
||||
self.decode = codecs.utf_16_be_decode
|
||||
elif consumed>=2:
|
||||
raise UnicodeError("UTF-16 stream does not start with BOM")
|
||||
raise UnicodeDecodeError("utf-16", input, 0, 2, "Stream does not start with BOM")
|
||||
return (object, consumed)
|
||||
|
||||
### encodings module API
|
||||
|
||||
6
Lib/encodings/utf_32.py
vendored
6
Lib/encodings/utf_32.py
vendored
@@ -59,7 +59,7 @@ class IncrementalDecoder(codecs.BufferedIncrementalDecoder):
|
||||
elif byteorder == 1:
|
||||
self.decoder = codecs.utf_32_be_decode
|
||||
elif consumed >= 4:
|
||||
raise UnicodeError("UTF-32 stream does not start with BOM")
|
||||
raise UnicodeDecodeError("utf-32", input, 0, 4, "Stream does not start with BOM")
|
||||
return (output, consumed)
|
||||
return self.decoder(input, self.errors, final)
|
||||
|
||||
@@ -132,8 +132,8 @@ class StreamReader(codecs.StreamReader):
|
||||
self.decode = codecs.utf_32_le_decode
|
||||
elif byteorder == 1:
|
||||
self.decode = codecs.utf_32_be_decode
|
||||
elif consumed>=4:
|
||||
raise UnicodeError("UTF-32 stream does not start with BOM")
|
||||
elif consumed >= 4:
|
||||
raise UnicodeDecodeError("utf-32", input, 0, 4, "Stream does not start with BOM")
|
||||
return (object, consumed)
|
||||
|
||||
### encodings module API
|
||||
|
||||
37
Lib/fnmatch.py
vendored
37
Lib/fnmatch.py
vendored
@@ -16,12 +16,6 @@ import functools
|
||||
|
||||
__all__ = ["filter", "fnmatch", "fnmatchcase", "translate"]
|
||||
|
||||
# Build a thread-safe incrementing counter to help create unique regexp group
|
||||
# names across calls.
|
||||
from itertools import count
|
||||
_nextgroupnum = count().__next__
|
||||
del count
|
||||
|
||||
def fnmatch(name, pat):
|
||||
"""Test whether FILENAME matches PATTERN.
|
||||
|
||||
@@ -41,7 +35,7 @@ def fnmatch(name, pat):
|
||||
pat = os.path.normcase(pat)
|
||||
return fnmatchcase(name, pat)
|
||||
|
||||
@functools.lru_cache(maxsize=256, typed=True)
|
||||
@functools.lru_cache(maxsize=32768, typed=True)
|
||||
def _compile_pattern(pat):
|
||||
if isinstance(pat, bytes):
|
||||
pat_str = str(pat, 'ISO-8859-1')
|
||||
@@ -84,6 +78,11 @@ def translate(pat):
|
||||
"""
|
||||
|
||||
STAR = object()
|
||||
parts = _translate(pat, STAR, '.')
|
||||
return _join_translated_parts(parts, STAR)
|
||||
|
||||
|
||||
def _translate(pat, STAR, QUESTION_MARK):
|
||||
res = []
|
||||
add = res.append
|
||||
i, n = 0, len(pat)
|
||||
@@ -95,7 +94,7 @@ def translate(pat):
|
||||
if (not res) or res[-1] is not STAR:
|
||||
add(STAR)
|
||||
elif c == '?':
|
||||
add('.')
|
||||
add(QUESTION_MARK)
|
||||
elif c == '[':
|
||||
j = i
|
||||
if j < n and pat[j] == '!':
|
||||
@@ -152,9 +151,11 @@ def translate(pat):
|
||||
else:
|
||||
add(re.escape(c))
|
||||
assert i == n
|
||||
return res
|
||||
|
||||
|
||||
def _join_translated_parts(inp, STAR):
|
||||
# Deal with STARs.
|
||||
inp = res
|
||||
res = []
|
||||
add = res.append
|
||||
i, n = 0, len(inp)
|
||||
@@ -165,17 +166,10 @@ def translate(pat):
|
||||
# Now deal with STAR fixed STAR fixed ...
|
||||
# For an interior `STAR fixed` pairing, we want to do a minimal
|
||||
# .*? match followed by `fixed`, with no possibility of backtracking.
|
||||
# We can't spell that directly, but can trick it into working by matching
|
||||
# .*?fixed
|
||||
# in a lookahead assertion, save the matched part in a group, then
|
||||
# consume that group via a backreference. If the overall match fails,
|
||||
# the lookahead assertion won't try alternatives. So the translation is:
|
||||
# (?=(?P<name>.*?fixed))(?P=name)
|
||||
# Group names are created as needed: g0, g1, g2, ...
|
||||
# The numbers are obtained from _nextgroupnum() to ensure they're unique
|
||||
# across calls and across threads. This is because people rely on the
|
||||
# undocumented ability to join multiple translate() results together via
|
||||
# "|" to build large regexps matching "one of many" shell patterns.
|
||||
# Atomic groups ("(?>...)") allow us to spell that directly.
|
||||
# Note: people rely on the undocumented ability to join multiple
|
||||
# translate() results together via "|" to build large regexps matching
|
||||
# "one of many" shell patterns.
|
||||
while i < n:
|
||||
assert inp[i] is STAR
|
||||
i += 1
|
||||
@@ -192,8 +186,7 @@ def translate(pat):
|
||||
add(".*")
|
||||
add(fixed)
|
||||
else:
|
||||
groupnum = _nextgroupnum()
|
||||
add(f"(?=(?P<g{groupnum}>.*?{fixed}))(?P=g{groupnum})")
|
||||
add(f"(?>.*?{fixed})")
|
||||
assert i == n
|
||||
res = "".join(res)
|
||||
return fr'(?s:{res})\Z'
|
||||
|
||||
301
Lib/glob.py
vendored
301
Lib/glob.py
vendored
@@ -4,11 +4,14 @@ import contextlib
|
||||
import os
|
||||
import re
|
||||
import fnmatch
|
||||
import functools
|
||||
import itertools
|
||||
import operator
|
||||
import stat
|
||||
import sys
|
||||
|
||||
__all__ = ["glob", "iglob", "escape"]
|
||||
|
||||
__all__ = ["glob", "iglob", "escape", "translate"]
|
||||
|
||||
def glob(pathname, *, root_dir=None, dir_fd=None, recursive=False,
|
||||
include_hidden=False):
|
||||
@@ -104,8 +107,8 @@ def _iglob(pathname, root_dir, dir_fd, recursive, dironly,
|
||||
|
||||
def _glob1(dirname, pattern, dir_fd, dironly, include_hidden=False):
|
||||
names = _listdir(dirname, dir_fd, dironly)
|
||||
if include_hidden or not _ishidden(pattern):
|
||||
names = (x for x in names if include_hidden or not _ishidden(x))
|
||||
if not (include_hidden or _ishidden(pattern)):
|
||||
names = (x for x in names if not _ishidden(x))
|
||||
return fnmatch.filter(names, pattern)
|
||||
|
||||
def _glob0(dirname, basename, dir_fd, dironly, include_hidden=False):
|
||||
@@ -119,12 +122,19 @@ def _glob0(dirname, basename, dir_fd, dironly, include_hidden=False):
|
||||
return [basename]
|
||||
return []
|
||||
|
||||
# Following functions are not public but can be used by third-party code.
|
||||
_deprecated_function_message = (
|
||||
"{name} is deprecated and will be removed in Python {remove}. Use "
|
||||
"glob.glob and pass a directory to its root_dir argument instead."
|
||||
)
|
||||
|
||||
def glob0(dirname, pattern):
|
||||
import warnings
|
||||
warnings._deprecated("glob.glob0", _deprecated_function_message, remove=(3, 15))
|
||||
return _glob0(dirname, pattern, None, False)
|
||||
|
||||
def glob1(dirname, pattern):
|
||||
import warnings
|
||||
warnings._deprecated("glob.glob1", _deprecated_function_message, remove=(3, 15))
|
||||
return _glob1(dirname, pattern, None, False)
|
||||
|
||||
# This helper function recursively yields relative pathnames inside a literal
|
||||
@@ -249,4 +259,287 @@ def escape(pathname):
|
||||
return drive + pathname
|
||||
|
||||
|
||||
_special_parts = ('', '.', '..')
|
||||
_dir_open_flags = os.O_RDONLY | getattr(os, 'O_DIRECTORY', 0)
|
||||
_no_recurse_symlinks = object()
|
||||
|
||||
|
||||
def translate(pat, *, recursive=False, include_hidden=False, seps=None):
|
||||
"""Translate a pathname with shell wildcards to a regular expression.
|
||||
|
||||
If `recursive` is true, the pattern segment '**' will match any number of
|
||||
path segments.
|
||||
|
||||
If `include_hidden` is true, wildcards can match path segments beginning
|
||||
with a dot ('.').
|
||||
|
||||
If a sequence of separator characters is given to `seps`, they will be
|
||||
used to split the pattern into segments and match path separators. If not
|
||||
given, os.path.sep and os.path.altsep (where available) are used.
|
||||
"""
|
||||
if not seps:
|
||||
if os.path.altsep:
|
||||
seps = (os.path.sep, os.path.altsep)
|
||||
else:
|
||||
seps = os.path.sep
|
||||
escaped_seps = ''.join(map(re.escape, seps))
|
||||
any_sep = f'[{escaped_seps}]' if len(seps) > 1 else escaped_seps
|
||||
not_sep = f'[^{escaped_seps}]'
|
||||
if include_hidden:
|
||||
one_last_segment = f'{not_sep}+'
|
||||
one_segment = f'{one_last_segment}{any_sep}'
|
||||
any_segments = f'(?:.+{any_sep})?'
|
||||
any_last_segments = '.*'
|
||||
else:
|
||||
one_last_segment = f'[^{escaped_seps}.]{not_sep}*'
|
||||
one_segment = f'{one_last_segment}{any_sep}'
|
||||
any_segments = f'(?:{one_segment})*'
|
||||
any_last_segments = f'{any_segments}(?:{one_last_segment})?'
|
||||
|
||||
results = []
|
||||
parts = re.split(any_sep, pat)
|
||||
last_part_idx = len(parts) - 1
|
||||
for idx, part in enumerate(parts):
|
||||
if part == '*':
|
||||
results.append(one_segment if idx < last_part_idx else one_last_segment)
|
||||
elif recursive and part == '**':
|
||||
if idx < last_part_idx:
|
||||
if parts[idx + 1] != '**':
|
||||
results.append(any_segments)
|
||||
else:
|
||||
results.append(any_last_segments)
|
||||
else:
|
||||
if part:
|
||||
if not include_hidden and part[0] in '*?':
|
||||
results.append(r'(?!\.)')
|
||||
results.extend(fnmatch._translate(part, f'{not_sep}*', not_sep))
|
||||
if idx < last_part_idx:
|
||||
results.append(any_sep)
|
||||
res = ''.join(results)
|
||||
return fr'(?s:{res})\Z'
|
||||
|
||||
|
||||
@functools.lru_cache(maxsize=512)
|
||||
def _compile_pattern(pat, sep, case_sensitive, recursive=True):
|
||||
"""Compile given glob pattern to a re.Pattern object (observing case
|
||||
sensitivity)."""
|
||||
flags = re.NOFLAG if case_sensitive else re.IGNORECASE
|
||||
regex = translate(pat, recursive=recursive, include_hidden=True, seps=sep)
|
||||
return re.compile(regex, flags=flags).match
|
||||
|
||||
|
||||
class _Globber:
|
||||
"""Class providing shell-style pattern matching and globbing.
|
||||
"""
|
||||
|
||||
def __init__(self, sep, case_sensitive, case_pedantic=False, recursive=False):
|
||||
self.sep = sep
|
||||
self.case_sensitive = case_sensitive
|
||||
self.case_pedantic = case_pedantic
|
||||
self.recursive = recursive
|
||||
|
||||
# Low-level methods
|
||||
|
||||
lstat = operator.methodcaller('lstat')
|
||||
add_slash = operator.methodcaller('joinpath', '')
|
||||
|
||||
@staticmethod
|
||||
def scandir(path):
|
||||
"""Emulates os.scandir(), which returns an object that can be used as
|
||||
a context manager. This method is called by walk() and glob().
|
||||
"""
|
||||
return contextlib.nullcontext(path.iterdir())
|
||||
|
||||
@staticmethod
|
||||
def concat_path(path, text):
|
||||
"""Appends text to the given path.
|
||||
"""
|
||||
return path.with_segments(path._raw_path + text)
|
||||
|
||||
@staticmethod
|
||||
def parse_entry(entry):
|
||||
"""Returns the path of an entry yielded from scandir().
|
||||
"""
|
||||
return entry
|
||||
|
||||
# High-level methods
|
||||
|
||||
def compile(self, pat):
|
||||
return _compile_pattern(pat, self.sep, self.case_sensitive, self.recursive)
|
||||
|
||||
def selector(self, parts):
|
||||
"""Returns a function that selects from a given path, walking and
|
||||
filtering according to the glob-style pattern parts in *parts*.
|
||||
"""
|
||||
if not parts:
|
||||
return self.select_exists
|
||||
part = parts.pop()
|
||||
if self.recursive and part == '**':
|
||||
selector = self.recursive_selector
|
||||
elif part in _special_parts:
|
||||
selector = self.special_selector
|
||||
elif not self.case_pedantic and magic_check.search(part) is None:
|
||||
selector = self.literal_selector
|
||||
else:
|
||||
selector = self.wildcard_selector
|
||||
return selector(part, parts)
|
||||
|
||||
def special_selector(self, part, parts):
|
||||
"""Returns a function that selects special children of the given path.
|
||||
"""
|
||||
select_next = self.selector(parts)
|
||||
|
||||
def select_special(path, exists=False):
|
||||
path = self.concat_path(self.add_slash(path), part)
|
||||
return select_next(path, exists)
|
||||
return select_special
|
||||
|
||||
def literal_selector(self, part, parts):
|
||||
"""Returns a function that selects a literal descendant of a path.
|
||||
"""
|
||||
|
||||
# Optimization: consume and join any subsequent literal parts here,
|
||||
# rather than leaving them for the next selector. This reduces the
|
||||
# number of string concatenation operations and calls to add_slash().
|
||||
while parts and magic_check.search(parts[-1]) is None:
|
||||
part += self.sep + parts.pop()
|
||||
|
||||
select_next = self.selector(parts)
|
||||
|
||||
def select_literal(path, exists=False):
|
||||
path = self.concat_path(self.add_slash(path), part)
|
||||
return select_next(path, exists=False)
|
||||
return select_literal
|
||||
|
||||
def wildcard_selector(self, part, parts):
|
||||
"""Returns a function that selects direct children of a given path,
|
||||
filtering by pattern.
|
||||
"""
|
||||
|
||||
match = None if part == '*' else self.compile(part)
|
||||
dir_only = bool(parts)
|
||||
if dir_only:
|
||||
select_next = self.selector(parts)
|
||||
|
||||
def select_wildcard(path, exists=False):
|
||||
try:
|
||||
# We must close the scandir() object before proceeding to
|
||||
# avoid exhausting file descriptors when globbing deep trees.
|
||||
with self.scandir(path) as scandir_it:
|
||||
entries = list(scandir_it)
|
||||
except OSError:
|
||||
pass
|
||||
else:
|
||||
for entry in entries:
|
||||
if match is None or match(entry.name):
|
||||
if dir_only:
|
||||
try:
|
||||
if not entry.is_dir():
|
||||
continue
|
||||
except OSError:
|
||||
continue
|
||||
entry_path = self.parse_entry(entry)
|
||||
if dir_only:
|
||||
yield from select_next(entry_path, exists=True)
|
||||
else:
|
||||
yield entry_path
|
||||
return select_wildcard
|
||||
|
||||
def recursive_selector(self, part, parts):
|
||||
"""Returns a function that selects a given path and all its children,
|
||||
recursively, filtering by pattern.
|
||||
"""
|
||||
# Optimization: consume following '**' parts, which have no effect.
|
||||
while parts and parts[-1] == '**':
|
||||
parts.pop()
|
||||
|
||||
# Optimization: consume and join any following non-special parts here,
|
||||
# rather than leaving them for the next selector. They're used to
|
||||
# build a regular expression, which we use to filter the results of
|
||||
# the recursive walk. As a result, non-special pattern segments
|
||||
# following a '**' wildcard don't require additional filesystem access
|
||||
# to expand.
|
||||
follow_symlinks = self.recursive is not _no_recurse_symlinks
|
||||
if follow_symlinks:
|
||||
while parts and parts[-1] not in _special_parts:
|
||||
part += self.sep + parts.pop()
|
||||
|
||||
match = None if part == '**' else self.compile(part)
|
||||
dir_only = bool(parts)
|
||||
select_next = self.selector(parts)
|
||||
|
||||
def select_recursive(path, exists=False):
|
||||
path = self.add_slash(path)
|
||||
match_pos = len(str(path))
|
||||
if match is None or match(str(path), match_pos):
|
||||
yield from select_next(path, exists)
|
||||
stack = [path]
|
||||
while stack:
|
||||
yield from select_recursive_step(stack, match_pos)
|
||||
|
||||
def select_recursive_step(stack, match_pos):
|
||||
path = stack.pop()
|
||||
try:
|
||||
# We must close the scandir() object before proceeding to
|
||||
# avoid exhausting file descriptors when globbing deep trees.
|
||||
with self.scandir(path) as scandir_it:
|
||||
entries = list(scandir_it)
|
||||
except OSError:
|
||||
pass
|
||||
else:
|
||||
for entry in entries:
|
||||
is_dir = False
|
||||
try:
|
||||
if entry.is_dir(follow_symlinks=follow_symlinks):
|
||||
is_dir = True
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
if is_dir or not dir_only:
|
||||
entry_path = self.parse_entry(entry)
|
||||
if match is None or match(str(entry_path), match_pos):
|
||||
if dir_only:
|
||||
yield from select_next(entry_path, exists=True)
|
||||
else:
|
||||
# Optimization: directly yield the path if this is
|
||||
# last pattern part.
|
||||
yield entry_path
|
||||
if is_dir:
|
||||
stack.append(entry_path)
|
||||
|
||||
return select_recursive
|
||||
|
||||
def select_exists(self, path, exists=False):
|
||||
"""Yields the given path, if it exists.
|
||||
"""
|
||||
if exists:
|
||||
# Optimization: this path is already known to exist, e.g. because
|
||||
# it was returned from os.scandir(), so we skip calling lstat().
|
||||
yield path
|
||||
else:
|
||||
try:
|
||||
self.lstat(path)
|
||||
yield path
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
class _StringGlobber(_Globber):
|
||||
lstat = staticmethod(os.lstat)
|
||||
scandir = staticmethod(os.scandir)
|
||||
parse_entry = operator.attrgetter('path')
|
||||
concat_path = operator.add
|
||||
|
||||
if os.name == 'nt':
|
||||
@staticmethod
|
||||
def add_slash(pathname):
|
||||
tail = os.path.splitroot(pathname)[2]
|
||||
if not tail or tail[-1] in '\\/':
|
||||
return pathname
|
||||
return f'{pathname}\\'
|
||||
else:
|
||||
@staticmethod
|
||||
def add_slash(pathname):
|
||||
if not pathname or pathname[-1] == '/':
|
||||
return pathname
|
||||
return f'{pathname}/'
|
||||
|
||||
15
Lib/io.py
vendored
15
Lib/io.py
vendored
@@ -46,23 +46,17 @@ __all__ = ["BlockingIOError", "open", "open_code", "IOBase", "RawIOBase",
|
||||
"BufferedReader", "BufferedWriter", "BufferedRWPair",
|
||||
"BufferedRandom", "TextIOBase", "TextIOWrapper",
|
||||
"UnsupportedOperation", "SEEK_SET", "SEEK_CUR", "SEEK_END",
|
||||
"DEFAULT_BUFFER_SIZE", "text_encoding",
|
||||
"IncrementalNewlineDecoder"
|
||||
]
|
||||
"DEFAULT_BUFFER_SIZE", "text_encoding", "IncrementalNewlineDecoder"]
|
||||
|
||||
|
||||
import _io
|
||||
import abc
|
||||
|
||||
from _io import (DEFAULT_BUFFER_SIZE, BlockingIOError, UnsupportedOperation,
|
||||
open, open_code, BytesIO, StringIO, BufferedReader,
|
||||
open, open_code, FileIO, BytesIO, StringIO, BufferedReader,
|
||||
BufferedWriter, BufferedRWPair, BufferedRandom,
|
||||
IncrementalNewlineDecoder, text_encoding, TextIOWrapper)
|
||||
|
||||
try:
|
||||
from _io import FileIO
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
# Pretend this exception was created here.
|
||||
UnsupportedOperation.__module__ = "io"
|
||||
@@ -87,10 +81,7 @@ class BufferedIOBase(_io._BufferedIOBase, IOBase):
|
||||
class TextIOBase(_io._TextIOBase, IOBase):
|
||||
__doc__ = _io._TextIOBase.__doc__
|
||||
|
||||
try:
|
||||
RawIOBase.register(FileIO)
|
||||
except NameError:
|
||||
pass
|
||||
RawIOBase.register(FileIO)
|
||||
|
||||
for klass in (BytesIO, BufferedReader, BufferedWriter, BufferedRandom,
|
||||
BufferedRWPair):
|
||||
|
||||
449
Lib/opcode.py
vendored
449
Lib/opcode.py
vendored
@@ -4,404 +4,47 @@ opcode module - potentially shared between dis and other modules which
|
||||
operate on bytecodes (e.g. peephole optimizers).
|
||||
"""
|
||||
|
||||
__all__ = ["cmp_op", "hasarg", "hasconst", "hasname", "hasjrel", "hasjabs",
|
||||
"haslocal", "hascompare", "hasfree", "hasexc", "opname", "opmap",
|
||||
"HAVE_ARGUMENT", "EXTENDED_ARG"]
|
||||
|
||||
# It's a chicken-and-egg I'm afraid:
|
||||
# We're imported before _opcode's made.
|
||||
# With exception unheeded
|
||||
# (stack_effect is not needed)
|
||||
# Both our chickens and eggs are allayed.
|
||||
# --Larry Hastings, 2013/11/23
|
||||
__all__ = ["cmp_op", "stack_effect", "hascompare", "opname", "opmap",
|
||||
"HAVE_ARGUMENT", "EXTENDED_ARG", "hasarg", "hasconst", "hasname",
|
||||
"hasjump", "hasjrel", "hasjabs", "hasfree", "haslocal", "hasexc"]
|
||||
|
||||
try:
|
||||
from _opcode import stack_effect
|
||||
__all__.append('stack_effect')
|
||||
except ImportError:
|
||||
pass
|
||||
import _opcode
|
||||
from _opcode import stack_effect
|
||||
|
||||
cmp_op = ('<', '<=', '==', '!=', '>', '>=')
|
||||
from _opcode_metadata import (_specializations, _specialized_opmap, opmap,
|
||||
HAVE_ARGUMENT, MIN_INSTRUMENTED_OPCODE)
|
||||
EXTENDED_ARG = opmap['EXTENDED_ARG']
|
||||
|
||||
hasarg = []
|
||||
hasconst = []
|
||||
hasname = []
|
||||
hasjrel = []
|
||||
hasjabs = []
|
||||
haslocal = []
|
||||
hascompare = []
|
||||
hasfree = []
|
||||
hasexc = []
|
||||
|
||||
def is_pseudo(op):
|
||||
return op >= MIN_PSEUDO_OPCODE and op <= MAX_PSEUDO_OPCODE
|
||||
|
||||
oplists = [hasarg, hasconst, hasname, hasjrel, hasjabs,
|
||||
haslocal, hascompare, hasfree, hasexc]
|
||||
|
||||
opmap = {}
|
||||
|
||||
## pseudo opcodes (used in the compiler) mapped to the values
|
||||
## they can become in the actual code.
|
||||
_pseudo_ops = {}
|
||||
|
||||
def def_op(name, op):
|
||||
opmap[name] = op
|
||||
|
||||
def name_op(name, op):
|
||||
def_op(name, op)
|
||||
hasname.append(op)
|
||||
|
||||
def jrel_op(name, op):
|
||||
def_op(name, op)
|
||||
hasjrel.append(op)
|
||||
|
||||
def jabs_op(name, op):
|
||||
def_op(name, op)
|
||||
hasjabs.append(op)
|
||||
|
||||
def pseudo_op(name, op, real_ops):
|
||||
def_op(name, op)
|
||||
_pseudo_ops[name] = real_ops
|
||||
# add the pseudo opcode to the lists its targets are in
|
||||
for oplist in oplists:
|
||||
res = [opmap[rop] in oplist for rop in real_ops]
|
||||
if any(res):
|
||||
assert all(res)
|
||||
oplist.append(op)
|
||||
|
||||
|
||||
# Instruction opcodes for compiled code
|
||||
# Blank lines correspond to available opcodes
|
||||
|
||||
def_op('CACHE', 0)
|
||||
def_op('POP_TOP', 1)
|
||||
def_op('PUSH_NULL', 2)
|
||||
|
||||
def_op('NOP', 9)
|
||||
def_op('UNARY_POSITIVE', 10)
|
||||
def_op('UNARY_NEGATIVE', 11)
|
||||
def_op('UNARY_NOT', 12)
|
||||
|
||||
def_op('UNARY_INVERT', 15)
|
||||
|
||||
def_op('BINARY_SUBSCR', 25)
|
||||
def_op('BINARY_SLICE', 26)
|
||||
def_op('STORE_SLICE', 27)
|
||||
|
||||
def_op('GET_LEN', 30)
|
||||
def_op('MATCH_MAPPING', 31)
|
||||
def_op('MATCH_SEQUENCE', 32)
|
||||
def_op('MATCH_KEYS', 33)
|
||||
|
||||
def_op('PUSH_EXC_INFO', 35)
|
||||
def_op('CHECK_EXC_MATCH', 36)
|
||||
def_op('CHECK_EG_MATCH', 37)
|
||||
|
||||
def_op('WITH_EXCEPT_START', 49)
|
||||
def_op('GET_AITER', 50)
|
||||
def_op('GET_ANEXT', 51)
|
||||
def_op('BEFORE_ASYNC_WITH', 52)
|
||||
def_op('BEFORE_WITH', 53)
|
||||
def_op('END_ASYNC_FOR', 54)
|
||||
def_op('CLEANUP_THROW', 55)
|
||||
|
||||
def_op('STORE_SUBSCR', 60)
|
||||
def_op('DELETE_SUBSCR', 61)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
# Delete below def_op after updating coroutines.py
|
||||
def_op('YIELD_FROM', 72)
|
||||
|
||||
def_op('GET_ITER', 68)
|
||||
def_op('GET_YIELD_FROM_ITER', 69)
|
||||
def_op('PRINT_EXPR', 70)
|
||||
def_op('LOAD_BUILD_CLASS', 71)
|
||||
|
||||
def_op('LOAD_ASSERTION_ERROR', 74)
|
||||
def_op('RETURN_GENERATOR', 75)
|
||||
|
||||
def_op('LIST_TO_TUPLE', 82)
|
||||
def_op('RETURN_VALUE', 83)
|
||||
def_op('IMPORT_STAR', 84)
|
||||
def_op('SETUP_ANNOTATIONS', 85)
|
||||
|
||||
def_op('ASYNC_GEN_WRAP', 87)
|
||||
def_op('PREP_RERAISE_STAR', 88)
|
||||
def_op('POP_EXCEPT', 89)
|
||||
|
||||
HAVE_ARGUMENT = 90 # real opcodes from here have an argument:
|
||||
|
||||
name_op('STORE_NAME', 90) # Index in name list
|
||||
name_op('DELETE_NAME', 91) # ""
|
||||
def_op('UNPACK_SEQUENCE', 92) # Number of tuple items
|
||||
jrel_op('FOR_ITER', 93)
|
||||
def_op('UNPACK_EX', 94)
|
||||
name_op('STORE_ATTR', 95) # Index in name list
|
||||
name_op('DELETE_ATTR', 96) # ""
|
||||
name_op('STORE_GLOBAL', 97) # ""
|
||||
name_op('DELETE_GLOBAL', 98) # ""
|
||||
def_op('SWAP', 99)
|
||||
def_op('LOAD_CONST', 100) # Index in const list
|
||||
hasconst.append(100)
|
||||
name_op('LOAD_NAME', 101) # Index in name list
|
||||
def_op('BUILD_TUPLE', 102) # Number of tuple items
|
||||
def_op('BUILD_LIST', 103) # Number of list items
|
||||
def_op('BUILD_SET', 104) # Number of set items
|
||||
def_op('BUILD_MAP', 105) # Number of dict entries
|
||||
name_op('LOAD_ATTR', 106) # Index in name list
|
||||
def_op('COMPARE_OP', 107) # Comparison operator
|
||||
hascompare.append(107)
|
||||
name_op('IMPORT_NAME', 108) # Index in name list
|
||||
name_op('IMPORT_FROM', 109) # Index in name list
|
||||
jrel_op('JUMP_FORWARD', 110) # Number of words to skip
|
||||
jrel_op('JUMP_IF_FALSE_OR_POP', 111) # Number of words to skip
|
||||
jrel_op('JUMP_IF_TRUE_OR_POP', 112) # ""
|
||||
jrel_op('POP_JUMP_IF_FALSE', 114)
|
||||
jrel_op('POP_JUMP_IF_TRUE', 115)
|
||||
name_op('LOAD_GLOBAL', 116) # Index in name list
|
||||
def_op('IS_OP', 117)
|
||||
def_op('CONTAINS_OP', 118)
|
||||
def_op('RERAISE', 119)
|
||||
def_op('COPY', 120)
|
||||
def_op('BINARY_OP', 122)
|
||||
jrel_op('SEND', 123) # Number of bytes to skip
|
||||
def_op('LOAD_FAST', 124) # Local variable number, no null check
|
||||
haslocal.append(124)
|
||||
def_op('STORE_FAST', 125) # Local variable number
|
||||
haslocal.append(125)
|
||||
def_op('DELETE_FAST', 126) # Local variable number
|
||||
haslocal.append(126)
|
||||
def_op('LOAD_FAST_CHECK', 127) # Local variable number
|
||||
haslocal.append(127)
|
||||
jrel_op('POP_JUMP_IF_NOT_NONE', 128)
|
||||
jrel_op('POP_JUMP_IF_NONE', 129)
|
||||
def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3)
|
||||
def_op('GET_AWAITABLE', 131)
|
||||
def_op('MAKE_FUNCTION', 132) # Flags
|
||||
def_op('BUILD_SLICE', 133) # Number of items
|
||||
jrel_op('JUMP_BACKWARD_NO_INTERRUPT', 134) # Number of words to skip (backwards)
|
||||
def_op('MAKE_CELL', 135)
|
||||
hasfree.append(135)
|
||||
def_op('LOAD_CLOSURE', 136)
|
||||
hasfree.append(136)
|
||||
def_op('LOAD_DEREF', 137)
|
||||
hasfree.append(137)
|
||||
def_op('STORE_DEREF', 138)
|
||||
hasfree.append(138)
|
||||
def_op('DELETE_DEREF', 139)
|
||||
hasfree.append(139)
|
||||
jrel_op('JUMP_BACKWARD', 140) # Number of words to skip (backwards)
|
||||
|
||||
def_op('CALL_FUNCTION_EX', 142) # Flags
|
||||
|
||||
def_op('EXTENDED_ARG', 144)
|
||||
EXTENDED_ARG = 144
|
||||
def_op('LIST_APPEND', 145)
|
||||
def_op('SET_ADD', 146)
|
||||
def_op('MAP_ADD', 147)
|
||||
def_op('LOAD_CLASSDEREF', 148)
|
||||
hasfree.append(148)
|
||||
def_op('COPY_FREE_VARS', 149)
|
||||
def_op('YIELD_VALUE', 150)
|
||||
def_op('RESUME', 151) # This must be kept in sync with deepfreeze.py
|
||||
def_op('MATCH_CLASS', 152)
|
||||
|
||||
def_op('FORMAT_VALUE', 155)
|
||||
def_op('BUILD_CONST_KEY_MAP', 156)
|
||||
def_op('BUILD_STRING', 157)
|
||||
|
||||
def_op('LIST_EXTEND', 162)
|
||||
def_op('SET_UPDATE', 163)
|
||||
def_op('DICT_MERGE', 164)
|
||||
def_op('DICT_UPDATE', 165)
|
||||
|
||||
def_op('CALL', 171)
|
||||
def_op('KW_NAMES', 172)
|
||||
hasconst.append(172)
|
||||
|
||||
|
||||
hasarg.extend([op for op in opmap.values() if op >= HAVE_ARGUMENT])
|
||||
|
||||
MIN_PSEUDO_OPCODE = 256
|
||||
|
||||
pseudo_op('SETUP_FINALLY', 256, ['NOP'])
|
||||
hasexc.append(256)
|
||||
pseudo_op('SETUP_CLEANUP', 257, ['NOP'])
|
||||
hasexc.append(257)
|
||||
pseudo_op('SETUP_WITH', 258, ['NOP'])
|
||||
hasexc.append(258)
|
||||
pseudo_op('POP_BLOCK', 259, ['NOP'])
|
||||
|
||||
pseudo_op('JUMP', 260, ['JUMP_FORWARD', 'JUMP_BACKWARD'])
|
||||
pseudo_op('JUMP_NO_INTERRUPT', 261, ['JUMP_FORWARD', 'JUMP_BACKWARD_NO_INTERRUPT'])
|
||||
|
||||
pseudo_op('LOAD_METHOD', 262, ['LOAD_ATTR'])
|
||||
|
||||
MAX_PSEUDO_OPCODE = MIN_PSEUDO_OPCODE + len(_pseudo_ops) - 1
|
||||
|
||||
del def_op, name_op, jrel_op, jabs_op, pseudo_op
|
||||
|
||||
opname = ['<%r>' % (op,) for op in range(MAX_PSEUDO_OPCODE + 1)]
|
||||
opname = ['<%r>' % (op,) for op in range(max(opmap.values()) + 1)]
|
||||
for op, i in opmap.items():
|
||||
opname[i] = op
|
||||
|
||||
cmp_op = ('<', '<=', '==', '!=', '>', '>=')
|
||||
|
||||
_nb_ops = [
|
||||
("NB_ADD", "+"),
|
||||
("NB_AND", "&"),
|
||||
("NB_FLOOR_DIVIDE", "//"),
|
||||
("NB_LSHIFT", "<<"),
|
||||
("NB_MATRIX_MULTIPLY", "@"),
|
||||
("NB_MULTIPLY", "*"),
|
||||
("NB_REMAINDER", "%"),
|
||||
("NB_OR", "|"),
|
||||
("NB_POWER", "**"),
|
||||
("NB_RSHIFT", ">>"),
|
||||
("NB_SUBTRACT", "-"),
|
||||
("NB_TRUE_DIVIDE", "/"),
|
||||
("NB_XOR", "^"),
|
||||
("NB_INPLACE_ADD", "+="),
|
||||
("NB_INPLACE_AND", "&="),
|
||||
("NB_INPLACE_FLOOR_DIVIDE", "//="),
|
||||
("NB_INPLACE_LSHIFT", "<<="),
|
||||
("NB_INPLACE_MATRIX_MULTIPLY", "@="),
|
||||
("NB_INPLACE_MULTIPLY", "*="),
|
||||
("NB_INPLACE_REMAINDER", "%="),
|
||||
("NB_INPLACE_OR", "|="),
|
||||
("NB_INPLACE_POWER", "**="),
|
||||
("NB_INPLACE_RSHIFT", ">>="),
|
||||
("NB_INPLACE_SUBTRACT", "-="),
|
||||
("NB_INPLACE_TRUE_DIVIDE", "/="),
|
||||
("NB_INPLACE_XOR", "^="),
|
||||
]
|
||||
# These lists are documented as part of the dis module's API
|
||||
hasarg = [op for op in opmap.values() if _opcode.has_arg(op)]
|
||||
hasconst = [op for op in opmap.values() if _opcode.has_const(op)]
|
||||
hasname = [op for op in opmap.values() if _opcode.has_name(op)]
|
||||
hasjump = [op for op in opmap.values() if _opcode.has_jump(op)]
|
||||
hasjrel = hasjump # for backward compatibility
|
||||
hasjabs = []
|
||||
hasfree = [op for op in opmap.values() if _opcode.has_free(op)]
|
||||
haslocal = [op for op in opmap.values() if _opcode.has_local(op)]
|
||||
hasexc = [op for op in opmap.values() if _opcode.has_exc(op)]
|
||||
|
||||
_specializations = {
|
||||
"BINARY_OP": [
|
||||
"BINARY_OP_ADAPTIVE",
|
||||
"BINARY_OP_ADD_FLOAT",
|
||||
"BINARY_OP_ADD_INT",
|
||||
"BINARY_OP_ADD_UNICODE",
|
||||
"BINARY_OP_INPLACE_ADD_UNICODE",
|
||||
"BINARY_OP_MULTIPLY_FLOAT",
|
||||
"BINARY_OP_MULTIPLY_INT",
|
||||
"BINARY_OP_SUBTRACT_FLOAT",
|
||||
"BINARY_OP_SUBTRACT_INT",
|
||||
],
|
||||
"BINARY_SUBSCR": [
|
||||
"BINARY_SUBSCR_ADAPTIVE",
|
||||
"BINARY_SUBSCR_DICT",
|
||||
"BINARY_SUBSCR_GETITEM",
|
||||
"BINARY_SUBSCR_LIST_INT",
|
||||
"BINARY_SUBSCR_TUPLE_INT",
|
||||
],
|
||||
"CALL": [
|
||||
"CALL_ADAPTIVE",
|
||||
"CALL_PY_EXACT_ARGS",
|
||||
"CALL_PY_WITH_DEFAULTS",
|
||||
"CALL_BOUND_METHOD_EXACT_ARGS",
|
||||
"CALL_BUILTIN_CLASS",
|
||||
"CALL_BUILTIN_FAST_WITH_KEYWORDS",
|
||||
"CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS",
|
||||
"CALL_NO_KW_BUILTIN_FAST",
|
||||
"CALL_NO_KW_BUILTIN_O",
|
||||
"CALL_NO_KW_ISINSTANCE",
|
||||
"CALL_NO_KW_LEN",
|
||||
"CALL_NO_KW_LIST_APPEND",
|
||||
"CALL_NO_KW_METHOD_DESCRIPTOR_FAST",
|
||||
"CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS",
|
||||
"CALL_NO_KW_METHOD_DESCRIPTOR_O",
|
||||
"CALL_NO_KW_STR_1",
|
||||
"CALL_NO_KW_TUPLE_1",
|
||||
"CALL_NO_KW_TYPE_1",
|
||||
],
|
||||
"COMPARE_OP": [
|
||||
"COMPARE_OP_ADAPTIVE",
|
||||
"COMPARE_OP_FLOAT_JUMP",
|
||||
"COMPARE_OP_INT_JUMP",
|
||||
"COMPARE_OP_STR_JUMP",
|
||||
],
|
||||
"EXTENDED_ARG": [
|
||||
"EXTENDED_ARG_QUICK",
|
||||
],
|
||||
"FOR_ITER": [
|
||||
"FOR_ITER_ADAPTIVE",
|
||||
"FOR_ITER_LIST",
|
||||
"FOR_ITER_RANGE",
|
||||
],
|
||||
"JUMP_BACKWARD": [
|
||||
"JUMP_BACKWARD_QUICK",
|
||||
],
|
||||
"LOAD_ATTR": [
|
||||
"LOAD_ATTR_ADAPTIVE",
|
||||
# These potentially push [NULL, bound method] onto the stack.
|
||||
"LOAD_ATTR_CLASS",
|
||||
"LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN",
|
||||
"LOAD_ATTR_INSTANCE_VALUE",
|
||||
"LOAD_ATTR_MODULE",
|
||||
"LOAD_ATTR_PROPERTY",
|
||||
"LOAD_ATTR_SLOT",
|
||||
"LOAD_ATTR_WITH_HINT",
|
||||
# These will always push [unbound method, self] onto the stack.
|
||||
"LOAD_ATTR_METHOD_LAZY_DICT",
|
||||
"LOAD_ATTR_METHOD_NO_DICT",
|
||||
"LOAD_ATTR_METHOD_WITH_DICT",
|
||||
"LOAD_ATTR_METHOD_WITH_VALUES",
|
||||
],
|
||||
"LOAD_CONST": [
|
||||
"LOAD_CONST__LOAD_FAST",
|
||||
],
|
||||
"LOAD_FAST": [
|
||||
"LOAD_FAST__LOAD_CONST",
|
||||
"LOAD_FAST__LOAD_FAST",
|
||||
],
|
||||
"LOAD_GLOBAL": [
|
||||
"LOAD_GLOBAL_ADAPTIVE",
|
||||
"LOAD_GLOBAL_BUILTIN",
|
||||
"LOAD_GLOBAL_MODULE",
|
||||
],
|
||||
"RESUME": [
|
||||
"RESUME_QUICK",
|
||||
],
|
||||
"STORE_ATTR": [
|
||||
"STORE_ATTR_ADAPTIVE",
|
||||
"STORE_ATTR_INSTANCE_VALUE",
|
||||
"STORE_ATTR_SLOT",
|
||||
"STORE_ATTR_WITH_HINT",
|
||||
],
|
||||
"STORE_FAST": [
|
||||
"STORE_FAST__LOAD_FAST",
|
||||
"STORE_FAST__STORE_FAST",
|
||||
],
|
||||
"STORE_SUBSCR": [
|
||||
"STORE_SUBSCR_ADAPTIVE",
|
||||
"STORE_SUBSCR_DICT",
|
||||
"STORE_SUBSCR_LIST_INT",
|
||||
],
|
||||
"UNPACK_SEQUENCE": [
|
||||
"UNPACK_SEQUENCE_ADAPTIVE",
|
||||
"UNPACK_SEQUENCE_LIST",
|
||||
"UNPACK_SEQUENCE_TUPLE",
|
||||
"UNPACK_SEQUENCE_TWO_TUPLE",
|
||||
],
|
||||
}
|
||||
_specialized_instructions = [
|
||||
opcode for family in _specializations.values() for opcode in family
|
||||
]
|
||||
_specialization_stats = [
|
||||
"success",
|
||||
"failure",
|
||||
"hit",
|
||||
"deferred",
|
||||
"miss",
|
||||
"deopt",
|
||||
]
|
||||
|
||||
_intrinsic_1_descs = _opcode.get_intrinsic1_descs()
|
||||
_intrinsic_2_descs = _opcode.get_intrinsic2_descs()
|
||||
_nb_ops = _opcode.get_nb_ops()
|
||||
|
||||
hascompare = [opmap["COMPARE_OP"]]
|
||||
|
||||
_cache_format = {
|
||||
"LOAD_GLOBAL": {
|
||||
"counter": 1,
|
||||
"index": 1,
|
||||
"module_keys_version": 2,
|
||||
"module_keys_version": 1,
|
||||
"builtin_keys_version": 1,
|
||||
},
|
||||
"BINARY_OP": {
|
||||
@@ -412,16 +55,19 @@ _cache_format = {
|
||||
},
|
||||
"COMPARE_OP": {
|
||||
"counter": 1,
|
||||
"mask": 1,
|
||||
},
|
||||
"CONTAINS_OP": {
|
||||
"counter": 1,
|
||||
},
|
||||
"BINARY_SUBSCR": {
|
||||
"counter": 1,
|
||||
"type_version": 2,
|
||||
"func_version": 1,
|
||||
},
|
||||
"FOR_ITER": {
|
||||
"counter": 1,
|
||||
},
|
||||
"LOAD_SUPER_ATTR": {
|
||||
"counter": 1,
|
||||
},
|
||||
"LOAD_ATTR": {
|
||||
"counter": 1,
|
||||
"version": 2,
|
||||
@@ -436,13 +82,34 @@ _cache_format = {
|
||||
"CALL": {
|
||||
"counter": 1,
|
||||
"func_version": 2,
|
||||
"min_args": 1,
|
||||
},
|
||||
"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,
|
||||
},
|
||||
}
|
||||
|
||||
_inline_cache_entries = [
|
||||
sum(_cache_format.get(opname[opcode], {}).values()) for opcode in range(256)
|
||||
]
|
||||
_inline_cache_entries = {
|
||||
name : sum(value.values()) for (name, value) in _cache_format.items()
|
||||
}
|
||||
|
||||
2
Lib/test/support/__init__.py
vendored
2
Lib/test/support/__init__.py
vendored
@@ -7,7 +7,7 @@ import contextlib
|
||||
import dataclasses
|
||||
import functools
|
||||
import logging
|
||||
# import _opcode # TODO: RUSTPYTHON
|
||||
import _opcode
|
||||
import os
|
||||
import re
|
||||
import stat
|
||||
|
||||
107
Lib/test/support/bytecode_helper.py
vendored
107
Lib/test/support/bytecode_helper.py
vendored
@@ -3,10 +3,26 @@
|
||||
import unittest
|
||||
import dis
|
||||
import io
|
||||
from _testinternalcapi import compiler_codegen, optimize_cfg, assemble_code_object
|
||||
import opcode
|
||||
try:
|
||||
import _testinternalcapi
|
||||
except ImportError:
|
||||
_testinternalcapi = None
|
||||
|
||||
_UNSPECIFIED = object()
|
||||
|
||||
def instructions_with_positions(instrs, co_positions):
|
||||
# Return (instr, positions) pairs from the instrs list and co_positions
|
||||
# iterator. The latter contains items for cache lines and the former
|
||||
# doesn't, so those need to be skipped.
|
||||
|
||||
co_positions = co_positions or iter(())
|
||||
for instr in instrs:
|
||||
yield instr, next(co_positions, ())
|
||||
for _, size, _ in (instr.cache_info or ()):
|
||||
for i in range(size):
|
||||
next(co_positions, ())
|
||||
|
||||
class BytecodeTestCase(unittest.TestCase):
|
||||
"""Custom assertion methods for inspecting bytecode."""
|
||||
|
||||
@@ -53,16 +69,14 @@ class CompilationStepTestCase(unittest.TestCase):
|
||||
class Label:
|
||||
pass
|
||||
|
||||
def assertInstructionsMatch(self, actual_, expected_):
|
||||
# get two lists where each entry is a label or
|
||||
# an instruction tuple. Normalize the labels to the
|
||||
# instruction count of the target, and compare the lists.
|
||||
def assertInstructionsMatch(self, actual_seq, expected):
|
||||
# get an InstructionSequence and an expected list, where each
|
||||
# entry is a label or an instruction tuple. Construct an expcted
|
||||
# instruction sequence and compare with the one given.
|
||||
|
||||
self.assertIsInstance(actual_, list)
|
||||
self.assertIsInstance(expected_, list)
|
||||
|
||||
actual = self.normalize_insts(actual_)
|
||||
expected = self.normalize_insts(expected_)
|
||||
self.assertIsInstance(expected, list)
|
||||
actual = actual_seq.get_instructions()
|
||||
expected = self.seq_from_insts(expected).get_instructions()
|
||||
self.assertEqual(len(actual), len(expected))
|
||||
|
||||
# compare instructions
|
||||
@@ -72,10 +86,8 @@ class CompilationStepTestCase(unittest.TestCase):
|
||||
continue
|
||||
self.assertIsInstance(exp, tuple)
|
||||
self.assertIsInstance(act, tuple)
|
||||
# crop comparison to the provided expected values
|
||||
if len(act) > len(exp):
|
||||
act = act[:len(exp)]
|
||||
self.assertEqual(exp, act)
|
||||
idx = max([p[0] for p in enumerate(exp) if p[1] != -1])
|
||||
self.assertEqual(exp[:idx], act[:idx])
|
||||
|
||||
def resolveAndRemoveLabels(self, insts):
|
||||
idx = 0
|
||||
@@ -90,54 +102,57 @@ class CompilationStepTestCase(unittest.TestCase):
|
||||
|
||||
return res
|
||||
|
||||
def normalize_insts(self, insts):
|
||||
""" Map labels to instruction index.
|
||||
Map opcodes to opnames.
|
||||
"""
|
||||
insts = self.resolveAndRemoveLabels(insts)
|
||||
res = []
|
||||
def seq_from_insts(self, insts):
|
||||
labels = {item for item in insts if isinstance(item, self.Label)}
|
||||
for i, lbl in enumerate(labels):
|
||||
lbl.value = i
|
||||
|
||||
seq = _testinternalcapi.new_instruction_sequence()
|
||||
for item in insts:
|
||||
assert isinstance(item, tuple)
|
||||
opcode, oparg, *loc = item
|
||||
opcode = dis.opmap.get(opcode, opcode)
|
||||
if isinstance(oparg, self.Label):
|
||||
arg = oparg.value
|
||||
if isinstance(item, self.Label):
|
||||
seq.use_label(item.value)
|
||||
else:
|
||||
arg = oparg if opcode in self.HAS_ARG else None
|
||||
opcode = dis.opname[opcode]
|
||||
res.append((opcode, arg, *loc))
|
||||
return res
|
||||
op = item[0]
|
||||
if isinstance(op, str):
|
||||
op = opcode.opmap[op]
|
||||
arg, *loc = item[1:]
|
||||
if isinstance(arg, self.Label):
|
||||
arg = arg.value
|
||||
loc = loc + [-1] * (4 - len(loc))
|
||||
seq.addop(op, arg or 0, *loc)
|
||||
return seq
|
||||
|
||||
def complete_insts_info(self, insts):
|
||||
# fill in omitted fields in location, and oparg 0 for ops with no arg.
|
||||
res = []
|
||||
for item in insts:
|
||||
assert isinstance(item, tuple)
|
||||
inst = list(item)
|
||||
opcode = dis.opmap[inst[0]]
|
||||
oparg = inst[1]
|
||||
loc = inst[2:] + [-1] * (6 - len(inst))
|
||||
res.append((opcode, oparg, *loc))
|
||||
return res
|
||||
def check_instructions(self, insts):
|
||||
for inst in insts:
|
||||
if isinstance(inst, self.Label):
|
||||
continue
|
||||
op, arg, *loc = inst
|
||||
if isinstance(op, str):
|
||||
op = opcode.opmap[op]
|
||||
self.assertEqual(op in opcode.hasarg,
|
||||
arg is not None,
|
||||
f"{opcode.opname[op]=} {arg=}")
|
||||
self.assertTrue(all(isinstance(l, int) for l in loc))
|
||||
|
||||
|
||||
@unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi")
|
||||
class CodegenTestCase(CompilationStepTestCase):
|
||||
|
||||
def generate_code(self, ast):
|
||||
insts, _ = compiler_codegen(ast, "my_file.py", 0)
|
||||
insts, _ = _testinternalcapi.compiler_codegen(ast, "my_file.py", 0)
|
||||
return insts
|
||||
|
||||
|
||||
@unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi")
|
||||
class CfgOptimizationTestCase(CompilationStepTestCase):
|
||||
|
||||
def get_optimized(self, insts, consts, nlocals=0):
|
||||
insts = self.normalize_insts(insts)
|
||||
insts = self.complete_insts_info(insts)
|
||||
insts = optimize_cfg(insts, consts, nlocals)
|
||||
def get_optimized(self, seq, consts, nlocals=0):
|
||||
insts = _testinternalcapi.optimize_cfg(seq, consts, nlocals)
|
||||
return insts, consts
|
||||
|
||||
@unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi")
|
||||
class AssemblerTestCase(CompilationStepTestCase):
|
||||
|
||||
def get_code_object(self, filename, insts, metadata):
|
||||
co = assemble_code_object(filename, insts, metadata)
|
||||
co = _testinternalcapi.assemble_code_object(filename, insts, metadata)
|
||||
return co
|
||||
|
||||
143
Lib/test/test__opcode.py
vendored
Normal file
143
Lib/test/test__opcode.py
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
import dis
|
||||
from test.support.import_helper import import_module
|
||||
import unittest
|
||||
import opcode
|
||||
|
||||
_opcode = import_module("_opcode")
|
||||
from _opcode import stack_effect
|
||||
|
||||
|
||||
class OpListTests(unittest.TestCase):
|
||||
def check_bool_function_result(self, func, ops, expected):
|
||||
for op in ops:
|
||||
if isinstance(op, str):
|
||||
op = dis.opmap[op]
|
||||
with self.subTest(opcode=op, func=func):
|
||||
self.assertIsInstance(func(op), bool)
|
||||
self.assertEqual(func(op), expected)
|
||||
|
||||
def test_invalid_opcodes(self):
|
||||
invalid = [-100, -1, 255, 512, 513, 1000]
|
||||
self.check_bool_function_result(_opcode.is_valid, invalid, False)
|
||||
self.check_bool_function_result(_opcode.has_arg, invalid, False)
|
||||
self.check_bool_function_result(_opcode.has_const, invalid, False)
|
||||
self.check_bool_function_result(_opcode.has_name, invalid, False)
|
||||
self.check_bool_function_result(_opcode.has_jump, invalid, False)
|
||||
self.check_bool_function_result(_opcode.has_free, invalid, False)
|
||||
self.check_bool_function_result(_opcode.has_local, invalid, False)
|
||||
self.check_bool_function_result(_opcode.has_exc, invalid, False)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'dis' has no attribute 'opmap'
|
||||
def test_is_valid(self):
|
||||
names = [
|
||||
'CACHE',
|
||||
'POP_TOP',
|
||||
'IMPORT_NAME',
|
||||
'JUMP',
|
||||
'INSTRUMENTED_RETURN_VALUE',
|
||||
]
|
||||
opcodes = [dis.opmap[opname] for opname in names]
|
||||
self.check_bool_function_result(_opcode.is_valid, opcodes, True)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'dis' has no attribute 'hasarg'
|
||||
def test_oplists(self):
|
||||
def check_function(self, func, expected):
|
||||
for op in [-10, 520]:
|
||||
with self.subTest(opcode=op, func=func):
|
||||
res = func(op)
|
||||
self.assertIsInstance(res, bool)
|
||||
self.assertEqual(res, op in expected)
|
||||
|
||||
check_function(self, _opcode.has_arg, dis.hasarg)
|
||||
check_function(self, _opcode.has_const, dis.hasconst)
|
||||
check_function(self, _opcode.has_name, dis.hasname)
|
||||
check_function(self, _opcode.has_jump, dis.hasjump)
|
||||
check_function(self, _opcode.has_free, dis.hasfree)
|
||||
check_function(self, _opcode.has_local, dis.haslocal)
|
||||
check_function(self, _opcode.has_exc, dis.hasexc)
|
||||
|
||||
|
||||
class StackEffectTests(unittest.TestCase):
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_stack_effect(self):
|
||||
self.assertEqual(stack_effect(dis.opmap['POP_TOP']), -1)
|
||||
self.assertEqual(stack_effect(dis.opmap['BUILD_SLICE'], 0), -1)
|
||||
self.assertEqual(stack_effect(dis.opmap['BUILD_SLICE'], 1), -1)
|
||||
self.assertEqual(stack_effect(dis.opmap['BUILD_SLICE'], 3), -2)
|
||||
self.assertRaises(ValueError, stack_effect, 30000)
|
||||
# All defined opcodes
|
||||
has_arg = dis.hasarg
|
||||
for name, code in filter(lambda item: item[0] not in dis.deoptmap, dis.opmap.items()):
|
||||
if code >= opcode.MIN_INSTRUMENTED_OPCODE:
|
||||
continue
|
||||
with self.subTest(opname=name):
|
||||
stack_effect(code)
|
||||
stack_effect(code, 0)
|
||||
# All not defined opcodes
|
||||
for code in set(range(256)) - set(dis.opmap.values()):
|
||||
with self.subTest(opcode=code):
|
||||
self.assertRaises(ValueError, stack_effect, code)
|
||||
self.assertRaises(ValueError, stack_effect, code, 0)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_stack_effect_jump(self):
|
||||
FOR_ITER = dis.opmap['FOR_ITER']
|
||||
self.assertEqual(stack_effect(FOR_ITER, 0), 1)
|
||||
self.assertEqual(stack_effect(FOR_ITER, 0, jump=True), 1)
|
||||
self.assertEqual(stack_effect(FOR_ITER, 0, jump=False), 1)
|
||||
JUMP_FORWARD = dis.opmap['JUMP_FORWARD']
|
||||
self.assertEqual(stack_effect(JUMP_FORWARD, 0), 0)
|
||||
self.assertEqual(stack_effect(JUMP_FORWARD, 0, jump=True), 0)
|
||||
self.assertEqual(stack_effect(JUMP_FORWARD, 0, jump=False), 0)
|
||||
# All defined opcodes
|
||||
has_arg = dis.hasarg
|
||||
has_exc = dis.hasexc
|
||||
has_jump = dis.hasjabs + dis.hasjrel
|
||||
for name, code in filter(lambda item: item[0] not in dis.deoptmap, dis.opmap.items()):
|
||||
if code >= opcode.MIN_INSTRUMENTED_OPCODE:
|
||||
continue
|
||||
with self.subTest(opname=name):
|
||||
if code not in has_arg:
|
||||
common = stack_effect(code)
|
||||
jump = stack_effect(code, jump=True)
|
||||
nojump = stack_effect(code, jump=False)
|
||||
else:
|
||||
common = stack_effect(code, 0)
|
||||
jump = stack_effect(code, 0, jump=True)
|
||||
nojump = stack_effect(code, 0, jump=False)
|
||||
if code in has_jump or code in has_exc:
|
||||
self.assertEqual(common, max(jump, nojump))
|
||||
else:
|
||||
self.assertEqual(jump, common)
|
||||
self.assertEqual(nojump, common)
|
||||
|
||||
|
||||
class SpecializationStatsTests(unittest.TestCase):
|
||||
def test_specialization_stats(self):
|
||||
stat_names = ["success", "failure", "hit", "deferred", "miss", "deopt"]
|
||||
specialized_opcodes = [
|
||||
op.lower()
|
||||
for op in opcode._specializations
|
||||
if opcode._inline_cache_entries.get(op, 0)
|
||||
]
|
||||
self.assertIn('load_attr', specialized_opcodes)
|
||||
self.assertIn('binary_subscr', specialized_opcodes)
|
||||
|
||||
stats = _opcode.get_specialization_stats()
|
||||
if stats is not None:
|
||||
self.assertIsInstance(stats, dict)
|
||||
self.assertCountEqual(stats.keys(), specialized_opcodes)
|
||||
self.assertCountEqual(
|
||||
stats['load_attr'].keys(),
|
||||
stat_names + ['failure_kinds'])
|
||||
for sn in stat_names:
|
||||
self.assertIsInstance(stats['load_attr'][sn], int)
|
||||
self.assertIsInstance(
|
||||
stats['load_attr']['failure_kinds'],
|
||||
tuple)
|
||||
for v in stats['load_attr']['failure_kinds']:
|
||||
self.assertIsInstance(v, int)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
882
Lib/test/test_call.py
vendored
882
Lib/test/test_call.py
vendored
File diff suppressed because it is too large
Load Diff
12
Lib/test/test_code.py
vendored
12
Lib/test/test_code.py
vendored
@@ -347,8 +347,6 @@ class CodeTest(unittest.TestCase):
|
||||
newcode = code.replace(co_name="func") # Should not raise SystemError
|
||||
self.assertEqual(code, newcode)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_empty_linetable(self):
|
||||
def func():
|
||||
pass
|
||||
@@ -468,8 +466,6 @@ class CodeTest(unittest.TestCase):
|
||||
|
||||
# co_positions behavior when info is missing.
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
# @requires_debug_ranges()
|
||||
def test_co_positions_empty_linetable(self):
|
||||
def func():
|
||||
@@ -480,8 +476,6 @@ class CodeTest(unittest.TestCase):
|
||||
self.assertIsNone(line)
|
||||
self.assertEqual(end_line, new_code.co_firstlineno + 1)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_code_equality(self):
|
||||
def f():
|
||||
try:
|
||||
@@ -522,8 +516,6 @@ class CodeTest(unittest.TestCase):
|
||||
self.assertNotEqual(c, swapped)
|
||||
self.assertNotEqual(hash(c), hash(swapped))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_code_hash_uses_bytecode(self):
|
||||
c = (lambda x, y: x + y).__code__
|
||||
d = (lambda x, y: x * y).__code__
|
||||
@@ -735,8 +727,6 @@ class CodeLocationTest(unittest.TestCase):
|
||||
self.assertEqual(l1, l2)
|
||||
self.assertEqual(len(pos1), len(pos2))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_positions(self):
|
||||
self.check_positions(parse_location_table)
|
||||
self.check_positions(misshappen)
|
||||
@@ -751,8 +741,6 @@ class CodeLocationTest(unittest.TestCase):
|
||||
self.assertEqual(l1, l2)
|
||||
self.assertEqual(len(lines1), len(lines2))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_lines(self):
|
||||
self.check_lines(parse_location_table)
|
||||
self.check_lines(misshappen)
|
||||
|
||||
9
Lib/test/test_codecs.py
vendored
9
Lib/test/test_codecs.py
vendored
@@ -762,7 +762,6 @@ class UTF16Test(ReadTest, unittest.TestCase):
|
||||
f = reader(s)
|
||||
self.assertEqual(f.read(), "spamspam")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON;; UTF-16 stream does not start with BOM
|
||||
def test_badbom(self):
|
||||
s = io.BytesIO(b"\xff\xff")
|
||||
f = codecs.getreader(self.encoding)(s)
|
||||
@@ -1509,7 +1508,6 @@ class PunycodeTest(unittest.TestCase):
|
||||
puny = puny.decode("ascii").encode("ascii")
|
||||
self.assertEqual(uni, puny.decode("punycode"))
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; b'Pro\xffprostnemluvesky' != b'Pro\xffprostnemluvesky-uyb24dma41a'
|
||||
def test_decode_invalid(self):
|
||||
testcases = [
|
||||
(b"xn--w&", "strict", UnicodeDecodeError("punycode", b"", 5, 6, "")),
|
||||
@@ -1694,7 +1692,6 @@ nameprep_tests = [
|
||||
|
||||
|
||||
class NameprepTest(unittest.TestCase):
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; UnicodeError: Invalid character '\u1680'
|
||||
def test_nameprep(self):
|
||||
from encodings.idna import nameprep
|
||||
for pos, (orig, prepped) in enumerate(nameprep_tests):
|
||||
@@ -1732,7 +1729,6 @@ class IDNACodecTest(unittest.TestCase):
|
||||
("あさ.\u034f", UnicodeEncodeError("idna", "あさ.\u034f", 3, 4, "")),
|
||||
]
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; 'XN--pythn-mua.org.' != 'pythön.org.'
|
||||
def test_builtin_decode(self):
|
||||
self.assertEqual(str(b"python.org", "idna"), "python.org")
|
||||
self.assertEqual(str(b"python.org.", "idna"), "python.org.")
|
||||
@@ -1746,7 +1742,6 @@ class IDNACodecTest(unittest.TestCase):
|
||||
self.assertEqual(str(b"bugs.XN--pythn-mua.org.", "idna"),
|
||||
"bugs.pyth\xf6n.org.")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; 'ascii' != 'idna'
|
||||
def test_builtin_decode_invalid(self):
|
||||
for case, expected in self.invalid_decode_testcases:
|
||||
with self.subTest(case=case, expected=expected):
|
||||
@@ -1764,7 +1759,6 @@ class IDNACodecTest(unittest.TestCase):
|
||||
self.assertEqual("pyth\xf6n.org".encode("idna"), b"xn--pythn-mua.org")
|
||||
self.assertEqual("pyth\xf6n.org.".encode("idna"), b"xn--pythn-mua.org.")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; UnicodeError: label empty or too long
|
||||
def test_builtin_encode_invalid(self):
|
||||
for case, expected in self.invalid_encode_testcases:
|
||||
with self.subTest(case=case, expected=expected):
|
||||
@@ -1776,7 +1770,6 @@ class IDNACodecTest(unittest.TestCase):
|
||||
self.assertEqual(exc.start, expected.start)
|
||||
self.assertEqual(exc.end, expected.end)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; UnicodeError: label empty or too long
|
||||
def test_builtin_decode_length_limit(self):
|
||||
with self.assertRaisesRegex(UnicodeDecodeError, "way too long"):
|
||||
(b"xn--016c"+b"a"*1100).decode("idna")
|
||||
@@ -1818,7 +1811,6 @@ class IDNACodecTest(unittest.TestCase):
|
||||
self.assertEqual(decoder.decode(b"rg."), "org.")
|
||||
self.assertEqual(decoder.decode(b"", True), "")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; 'ascii' != 'idna'
|
||||
def test_incremental_decode_invalid(self):
|
||||
iterdecode_testcases = [
|
||||
(b"\xFFpython.org", UnicodeDecodeError("idna", b"\xFF", 0, 1, "")),
|
||||
@@ -1880,7 +1872,6 @@ class IDNACodecTest(unittest.TestCase):
|
||||
self.assertEqual(encoder.encode("ample.org."), b"xn--xample-9ta.org.")
|
||||
self.assertEqual(encoder.encode("", True), b"")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; UnicodeError: label empty or too long
|
||||
def test_incremental_encode_invalid(self):
|
||||
iterencode_testcases = [
|
||||
(f"foo.{'\xff'*60}", UnicodeEncodeError("idna", f"{'\xff'*60}", 0, 60, "")),
|
||||
|
||||
2
Lib/test/test_compile.py
vendored
2
Lib/test/test_compile.py
vendored
@@ -885,8 +885,6 @@ if 1:
|
||||
self.assertIn('LOAD_ATTR', instructions)
|
||||
self.assertIn('PRECALL', instructions)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_lineno_procedure_call(self):
|
||||
def call():
|
||||
(
|
||||
|
||||
5
Lib/test/test_file.py
vendored
5
Lib/test/test_file.py
vendored
@@ -344,10 +344,9 @@ class OtherFileTests:
|
||||
class COtherFileTests(OtherFileTests, unittest.TestCase):
|
||||
open = io.open
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def testSetBufferSize(self):
|
||||
super().testSetBufferSize()
|
||||
return super().testSetBufferSize()
|
||||
|
||||
class PyOtherFileTests(OtherFileTests, unittest.TestCase):
|
||||
open = staticmethod(pyio.open)
|
||||
|
||||
14
Lib/test/test_fnmatch.py
vendored
14
Lib/test/test_fnmatch.py
vendored
@@ -71,7 +71,7 @@ class FnmatchTestCase(unittest.TestCase):
|
||||
check('usr/bin', 'usr\\bin', False, fnmatchcase)
|
||||
check('usr\\bin', 'usr\\bin', True, fnmatchcase)
|
||||
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
|
||||
@unittest.expectedFailureIfWindows('TODO: RUSTPYTHON')
|
||||
def test_bytes(self):
|
||||
self.check_match(b'test', b'te*')
|
||||
self.check_match(b'test\xff', b'te*\xff')
|
||||
@@ -239,17 +239,9 @@ class TranslateTestCase(unittest.TestCase):
|
||||
self.assertEqual(translate('A*********?[?]?'), r'(?s:A.*.[?].)\Z')
|
||||
# fancy translation to prevent exponential-time match failure
|
||||
t = translate('**a*a****a')
|
||||
digits = re.findall(r'\d+', t)
|
||||
self.assertEqual(len(digits), 4)
|
||||
self.assertEqual(digits[0], digits[1])
|
||||
self.assertEqual(digits[2], digits[3])
|
||||
g1 = f"g{digits[0]}" # e.g., group name "g4"
|
||||
g2 = f"g{digits[2]}" # e.g., group name "g5"
|
||||
self.assertEqual(t,
|
||||
fr'(?s:(?=(?P<{g1}>.*?a))(?P={g1})(?=(?P<{g2}>.*?a))(?P={g2}).*a)\Z')
|
||||
self.assertEqual(t, r'(?s:(?>.*?a)(?>.*?a).*a)\Z')
|
||||
# and try pasting multiple translate results - it's an undocumented
|
||||
# feature that this works; all the pain of generating unique group
|
||||
# names across calls exists to support this
|
||||
# feature that this works
|
||||
r1 = translate('**a**a**a*')
|
||||
r2 = translate('**b**b**b*')
|
||||
r3 = translate('*c*c*c*')
|
||||
|
||||
1833
Lib/test/test_fstring.py
vendored
1833
Lib/test/test_fstring.py
vendored
File diff suppressed because it is too large
Load Diff
194
Lib/test/test_glob.py
vendored
194
Lib/test/test_glob.py
vendored
@@ -1,9 +1,12 @@
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
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,
|
||||
can_symlink, create_empty_file, change_cwd)
|
||||
|
||||
@@ -167,37 +170,45 @@ class GlobTests(unittest.TestCase):
|
||||
self.norm('aab', 'F')])
|
||||
|
||||
def test_glob_directory_with_trailing_slash(self):
|
||||
# Patterns ending with a slash shouldn't match non-dirs
|
||||
res = glob.glob(self.norm('Z*Z') + os.sep)
|
||||
self.assertEqual(res, [])
|
||||
res = glob.glob(self.norm('ZZZ') + os.sep)
|
||||
self.assertEqual(res, [])
|
||||
# When there is a wildcard pattern which ends with os.sep, glob()
|
||||
# doesn't blow up.
|
||||
res = glob.glob(self.norm('aa*') + os.sep)
|
||||
self.assertEqual(len(res), 2)
|
||||
# either of these results is reasonable
|
||||
self.assertIn(set(res), [
|
||||
{self.norm('aaa'), self.norm('aab')},
|
||||
{self.norm('aaa') + os.sep, self.norm('aab') + os.sep},
|
||||
])
|
||||
seps = (os.sep, os.altsep) if os.altsep else (os.sep,)
|
||||
for sep in seps:
|
||||
# Patterns ending with a slash shouldn't match non-dirs
|
||||
self.assertEqual(glob.glob(self.norm('Z*Z') + sep), [])
|
||||
self.assertEqual(glob.glob(self.norm('ZZZ') + sep), [])
|
||||
self.assertEqual(glob.glob(self.norm('aaa') + sep),
|
||||
[self.norm('aaa') + sep])
|
||||
# Preserving the redundant separators is an implementation detail.
|
||||
self.assertEqual(glob.glob(self.norm('aaa') + sep*2),
|
||||
[self.norm('aaa') + sep*2])
|
||||
# When there is a wildcard pattern which ends with a pathname
|
||||
# separator, glob() doesn't blow.
|
||||
# The result should end with the pathname separator.
|
||||
# Normalizing the trailing separator is an implementation detail.
|
||||
eq = self.assertSequencesEqual_noorder
|
||||
eq(glob.glob(self.norm('aa*') + sep),
|
||||
[self.norm('aaa') + os.sep, self.norm('aab') + os.sep])
|
||||
# Stripping the redundant separators is an implementation detail.
|
||||
eq(glob.glob(self.norm('aa*') + sep*2),
|
||||
[self.norm('aaa') + os.sep, self.norm('aab') + os.sep])
|
||||
|
||||
def test_glob_bytes_directory_with_trailing_slash(self):
|
||||
# Same as test_glob_directory_with_trailing_slash, but with a
|
||||
# bytes argument.
|
||||
res = glob.glob(os.fsencode(self.norm('Z*Z') + os.sep))
|
||||
self.assertEqual(res, [])
|
||||
res = glob.glob(os.fsencode(self.norm('ZZZ') + os.sep))
|
||||
self.assertEqual(res, [])
|
||||
res = glob.glob(os.fsencode(self.norm('aa*') + os.sep))
|
||||
self.assertEqual(len(res), 2)
|
||||
# either of these results is reasonable
|
||||
self.assertIn(set(res), [
|
||||
{os.fsencode(self.norm('aaa')),
|
||||
os.fsencode(self.norm('aab'))},
|
||||
{os.fsencode(self.norm('aaa') + os.sep),
|
||||
os.fsencode(self.norm('aab') + os.sep)},
|
||||
])
|
||||
seps = (os.sep, os.altsep) if os.altsep else (os.sep,)
|
||||
for sep in seps:
|
||||
self.assertEqual(glob.glob(os.fsencode(self.norm('Z*Z') + sep)), [])
|
||||
self.assertEqual(glob.glob(os.fsencode(self.norm('ZZZ') + sep)), [])
|
||||
self.assertEqual(glob.glob(os.fsencode(self.norm('aaa') + sep)),
|
||||
[os.fsencode(self.norm('aaa') + sep)])
|
||||
self.assertEqual(glob.glob(os.fsencode(self.norm('aaa') + sep*2)),
|
||||
[os.fsencode(self.norm('aaa') + sep*2)])
|
||||
eq = self.assertSequencesEqual_noorder
|
||||
eq(glob.glob(os.fsencode(self.norm('aa*') + sep)),
|
||||
[os.fsencode(self.norm('aaa') + os.sep),
|
||||
os.fsencode(self.norm('aab') + os.sep)])
|
||||
eq(glob.glob(os.fsencode(self.norm('aa*') + sep*2)),
|
||||
[os.fsencode(self.norm('aaa') + os.sep),
|
||||
os.fsencode(self.norm('aab') + os.sep)])
|
||||
|
||||
@skip_unless_symlink
|
||||
def test_glob_symlinks(self):
|
||||
@@ -205,8 +216,7 @@ class GlobTests(unittest.TestCase):
|
||||
eq(self.glob('sym3'), [self.norm('sym3')])
|
||||
eq(self.glob('sym3', '*'), [self.norm('sym3', 'EF'),
|
||||
self.norm('sym3', 'efg')])
|
||||
self.assertIn(self.glob('sym3' + os.sep),
|
||||
[[self.norm('sym3')], [self.norm('sym3') + os.sep]])
|
||||
eq(self.glob('sym3' + os.sep), [self.norm('sym3') + os.sep])
|
||||
eq(self.glob('*', '*F'),
|
||||
[self.norm('aaa', 'zzzF'),
|
||||
self.norm('aab', 'F'), self.norm('sym3', 'EF')])
|
||||
@@ -364,6 +374,8 @@ class GlobTests(unittest.TestCase):
|
||||
self.assertEqual(self.rglob('mypipe', 'sub'), [])
|
||||
self.assertEqual(self.rglob('mypipe', '*'), [])
|
||||
|
||||
|
||||
@unittest.skipIf(is_wasi and Py_DEBUG, "requires too much stack")
|
||||
def test_glob_many_open_files(self):
|
||||
depth = 30
|
||||
base = os.path.join(self.tempdir, 'deep')
|
||||
@@ -381,10 +393,134 @@ class GlobTests(unittest.TestCase):
|
||||
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'))
|
||||
self.assertIsNotNone(match('foo.bar'))
|
||||
self.assertIsNone(match('.foo'))
|
||||
match = re.compile(glob.translate('.*')).match
|
||||
self.assertIsNotNone(match('.foo'))
|
||||
match = re.compile(glob.translate('**', recursive=True)).match
|
||||
self.assertIsNotNone(match('foo'))
|
||||
self.assertIsNone(match('.foo'))
|
||||
self.assertIsNotNone(match(os.path.join('foo', 'bar')))
|
||||
self.assertIsNone(match(os.path.join('foo', '.bar')))
|
||||
self.assertIsNone(match(os.path.join('.foo', 'bar')))
|
||||
self.assertIsNone(match(os.path.join('.foo', '.bar')))
|
||||
match = re.compile(glob.translate('**/*', recursive=True)).match
|
||||
self.assertIsNotNone(match(os.path.join('foo', 'bar')))
|
||||
self.assertIsNone(match(os.path.join('foo', '.bar')))
|
||||
self.assertIsNone(match(os.path.join('.foo', 'bar')))
|
||||
self.assertIsNone(match(os.path.join('.foo', '.bar')))
|
||||
match = re.compile(glob.translate('*/**', recursive=True)).match
|
||||
self.assertIsNotNone(match(os.path.join('foo', 'bar')))
|
||||
self.assertIsNone(match(os.path.join('foo', '.bar')))
|
||||
self.assertIsNone(match(os.path.join('.foo', 'bar')))
|
||||
self.assertIsNone(match(os.path.join('.foo', '.bar')))
|
||||
match = re.compile(glob.translate('**/.bar', recursive=True)).match
|
||||
self.assertIsNotNone(match(os.path.join('foo', '.bar')))
|
||||
self.assertIsNone(match(os.path.join('.foo', '.bar')))
|
||||
match = re.compile(glob.translate('**/*.*', recursive=True)).match
|
||||
self.assertIsNone(match(os.path.join('foo', 'bar')))
|
||||
self.assertIsNone(match(os.path.join('foo', '.bar')))
|
||||
self.assertIsNotNone(match(os.path.join('foo', 'bar.txt')))
|
||||
self.assertIsNone(match(os.path.join('foo', '.bar.txt')))
|
||||
|
||||
def test_translate(self):
|
||||
def fn(pat):
|
||||
return glob.translate(pat, seps='/')
|
||||
self.assertEqual(fn('foo'), r'(?s:foo)\Z')
|
||||
self.assertEqual(fn('foo/bar'), r'(?s:foo/bar)\Z')
|
||||
self.assertEqual(fn('*'), r'(?s:[^/.][^/]*)\Z')
|
||||
self.assertEqual(fn('?'), r'(?s:(?!\.)[^/])\Z')
|
||||
self.assertEqual(fn('a*'), r'(?s:a[^/]*)\Z')
|
||||
self.assertEqual(fn('*a'), r'(?s:(?!\.)[^/]*a)\Z')
|
||||
self.assertEqual(fn('.*'), r'(?s:\.[^/]*)\Z')
|
||||
self.assertEqual(fn('?aa'), r'(?s:(?!\.)[^/]aa)\Z')
|
||||
self.assertEqual(fn('aa?'), r'(?s:aa[^/])\Z')
|
||||
self.assertEqual(fn('aa[ab]'), r'(?s:aa[ab])\Z')
|
||||
self.assertEqual(fn('**'), r'(?s:(?!\.)[^/]*)\Z')
|
||||
self.assertEqual(fn('***'), r'(?s:(?!\.)[^/]*)\Z')
|
||||
self.assertEqual(fn('a**'), r'(?s:a[^/]*)\Z')
|
||||
self.assertEqual(fn('**b'), r'(?s:(?!\.)[^/]*b)\Z')
|
||||
self.assertEqual(fn('/**/*/*.*/**'),
|
||||
r'(?s:/(?!\.)[^/]*/[^/.][^/]*/(?!\.)[^/]*\.[^/]*/(?!\.)[^/]*)\Z')
|
||||
|
||||
def test_translate_include_hidden(self):
|
||||
def fn(pat):
|
||||
return glob.translate(pat, include_hidden=True, seps='/')
|
||||
self.assertEqual(fn('foo'), r'(?s:foo)\Z')
|
||||
self.assertEqual(fn('foo/bar'), r'(?s:foo/bar)\Z')
|
||||
self.assertEqual(fn('*'), r'(?s:[^/]+)\Z')
|
||||
self.assertEqual(fn('?'), r'(?s:[^/])\Z')
|
||||
self.assertEqual(fn('a*'), r'(?s:a[^/]*)\Z')
|
||||
self.assertEqual(fn('*a'), r'(?s:[^/]*a)\Z')
|
||||
self.assertEqual(fn('.*'), r'(?s:\.[^/]*)\Z')
|
||||
self.assertEqual(fn('?aa'), r'(?s:[^/]aa)\Z')
|
||||
self.assertEqual(fn('aa?'), r'(?s:aa[^/])\Z')
|
||||
self.assertEqual(fn('aa[ab]'), r'(?s:aa[ab])\Z')
|
||||
self.assertEqual(fn('**'), r'(?s:[^/]*)\Z')
|
||||
self.assertEqual(fn('***'), r'(?s:[^/]*)\Z')
|
||||
self.assertEqual(fn('a**'), r'(?s:a[^/]*)\Z')
|
||||
self.assertEqual(fn('**b'), r'(?s:[^/]*b)\Z')
|
||||
self.assertEqual(fn('/**/*/*.*/**'), r'(?s:/[^/]*/[^/]+/[^/]*\.[^/]*/[^/]*)\Z')
|
||||
|
||||
def test_translate_recursive(self):
|
||||
def fn(pat):
|
||||
return glob.translate(pat, recursive=True, include_hidden=True, seps='/')
|
||||
self.assertEqual(fn('*'), r'(?s:[^/]+)\Z')
|
||||
self.assertEqual(fn('?'), r'(?s:[^/])\Z')
|
||||
self.assertEqual(fn('**'), r'(?s:.*)\Z')
|
||||
self.assertEqual(fn('**/**'), r'(?s:.*)\Z')
|
||||
self.assertEqual(fn('***'), r'(?s:[^/]*)\Z')
|
||||
self.assertEqual(fn('a**'), r'(?s:a[^/]*)\Z')
|
||||
self.assertEqual(fn('**b'), r'(?s:[^/]*b)\Z')
|
||||
self.assertEqual(fn('/**/*/*.*/**'), r'(?s:/(?:.+/)?[^/]+/[^/]*\.[^/]*/.*)\Z')
|
||||
|
||||
def test_translate_seps(self):
|
||||
def fn(pat):
|
||||
return glob.translate(pat, recursive=True, include_hidden=True, seps=['/', '\\'])
|
||||
self.assertEqual(fn('foo/bar\\baz'), r'(?s:foo[/\\]bar[/\\]baz)\Z')
|
||||
self.assertEqual(fn('**/*'), r'(?s:(?:.+[/\\])?[^/\\]+)\Z')
|
||||
|
||||
|
||||
@skip_unless_symlink
|
||||
class SymlinkLoopGlobTests(unittest.TestCase):
|
||||
|
||||
# gh-109959: On Linux, glob._isdir() and glob._lexists() can return False
|
||||
# randomly when checking the "link/" symbolic link.
|
||||
# https://github.com/python/cpython/issues/109959#issuecomment-2577550700
|
||||
@unittest.skip("flaky test")
|
||||
def test_selflink(self):
|
||||
tempdir = TESTFN + "_dir"
|
||||
os.makedirs(tempdir)
|
||||
|
||||
577
Lib/test/test_io.py
vendored
577
Lib/test/test_io.py
vendored
@@ -39,11 +39,9 @@ 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
|
||||
from test.support import os_helper
|
||||
from test.support import threading_helper
|
||||
from test.support import warnings_helper
|
||||
from test.support import skip_if_sanitizer
|
||||
from test.support import (
|
||||
import_helper, is_apple, os_helper, threading_helper, warnings_helper,
|
||||
)
|
||||
from test.support.os_helper import FakePath
|
||||
|
||||
import codecs
|
||||
@@ -66,10 +64,6 @@ else:
|
||||
class EmptyStruct(ctypes.Structure):
|
||||
pass
|
||||
|
||||
# Does io.IOBase finalizer log the exception if the close() method fails?
|
||||
# The exception is ignored silently by default in release build.
|
||||
IOBASE_EMITS_UNRAISABLE = (support.Py_DEBUG or sys.flags.dev_mode)
|
||||
|
||||
|
||||
def _default_chunk_size():
|
||||
"""Get the default TextIOWrapper chunk size"""
|
||||
@@ -631,10 +625,10 @@ class IOTest(unittest.TestCase):
|
||||
self.read_ops(f, True)
|
||||
|
||||
def test_large_file_ops(self):
|
||||
# On Windows and Mac OSX 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 sys.platform == 'darwin':
|
||||
# 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)
|
||||
@@ -645,11 +639,9 @@ class IOTest(unittest.TestCase):
|
||||
|
||||
def test_with_open(self):
|
||||
for bufsize in (0, 100):
|
||||
f = None
|
||||
with self.open(os_helper.TESTFN, "wb", bufsize) as f:
|
||||
f.write(b"xxx")
|
||||
self.assertEqual(f.closed, True)
|
||||
f = None
|
||||
try:
|
||||
with self.open(os_helper.TESTFN, "wb", bufsize) as f:
|
||||
1/0
|
||||
@@ -788,8 +780,7 @@ class IOTest(unittest.TestCase):
|
||||
file = self.open(f.fileno(), "r", encoding="utf-8", closefd=False)
|
||||
self.assertEqual(file.buffer.raw.closefd, False)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_garbage_collection(self):
|
||||
# FileIO objects are collected, and collecting them flushes
|
||||
# all data to disk.
|
||||
@@ -904,7 +895,7 @@ class IOTest(unittest.TestCase):
|
||||
def badopener(fname, flags):
|
||||
return -1
|
||||
with self.assertRaises(ValueError) as cm:
|
||||
open('non-existent', 'r', opener=badopener)
|
||||
self.open('non-existent', 'r', opener=badopener)
|
||||
self.assertEqual(str(cm.exception), 'opener returned -1')
|
||||
|
||||
def test_bad_opener_other_negative(self):
|
||||
@@ -912,7 +903,7 @@ class IOTest(unittest.TestCase):
|
||||
def badopener(fname, flags):
|
||||
return -2
|
||||
with self.assertRaises(ValueError) as cm:
|
||||
open('non-existent', 'r', opener=badopener)
|
||||
self.open('non-existent', 'r', opener=badopener)
|
||||
self.assertEqual(str(cm.exception), 'opener returned -2')
|
||||
|
||||
def test_opener_invalid_fd(self):
|
||||
@@ -1048,11 +1039,41 @@ class IOTest(unittest.TestCase):
|
||||
# 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):
|
||||
|
||||
# TODO: RUSTPYTHON, cyclic gc
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; cyclic gc
|
||||
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
|
||||
@@ -1071,10 +1092,9 @@ class CIOTest(IOTest):
|
||||
support.gc_collect()
|
||||
self.assertIsNone(wr(), wr)
|
||||
|
||||
# TODO: RUSTPYTHON, AssertionError: filter ('', ResourceWarning) did not catch any warning
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: filter ('', ResourceWarning) did not catch any warning
|
||||
def test_destructor(self):
|
||||
super().test_destructor(self)
|
||||
return super().test_destructor()
|
||||
|
||||
@support.cpython_only
|
||||
class TestIOCTypes(unittest.TestCase):
|
||||
@@ -1165,9 +1185,32 @@ class TestIOCTypes(unittest.TestCase):
|
||||
_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
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; OSError: Negative file descriptor
|
||||
def test_bad_opener_negative_1():
|
||||
return super().test_bad_opener_negative_1()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; OSError: Negative file descriptor
|
||||
def test_bad_opener_other_negative():
|
||||
return super().test_bad_opener_other_negative()
|
||||
|
||||
|
||||
@support.cpython_only
|
||||
class APIMismatchTest(unittest.TestCase):
|
||||
@@ -1175,7 +1218,7 @@ 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__',))
|
||||
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):
|
||||
@@ -1244,6 +1287,7 @@ class CommonBufferedTests:
|
||||
# a ValueError.
|
||||
self.assertRaises(ValueError, _with)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_error_through_destructor(self):
|
||||
# Test that the exception state is not modified by a destructor,
|
||||
# even if close() fails.
|
||||
@@ -1252,10 +1296,7 @@ class CommonBufferedTests:
|
||||
with self.assertRaises(AttributeError):
|
||||
self.tp(rawio).xyzzy
|
||||
|
||||
if not IOBASE_EMITS_UNRAISABLE:
|
||||
self.assertIsNone(cm.unraisable)
|
||||
elif cm.unraisable is not None:
|
||||
self.assertEqual(cm.unraisable.exc_type, OSError)
|
||||
self.assertEqual(cm.unraisable.exc_type, OSError)
|
||||
|
||||
def test_repr(self):
|
||||
raw = self.MockRawIO()
|
||||
@@ -1271,11 +1312,9 @@ class CommonBufferedTests:
|
||||
# Issue #25455
|
||||
raw = self.MockRawIO()
|
||||
b = self.tp(raw)
|
||||
with support.swap_attr(raw, 'name', b):
|
||||
try:
|
||||
with support.swap_attr(raw, 'name', b), support.infinite_recursion(25):
|
||||
with self.assertRaises(RuntimeError):
|
||||
repr(b) # Should not crash
|
||||
except RuntimeError:
|
||||
pass
|
||||
|
||||
def test_flush_error_on_close(self):
|
||||
# Test that buffered file is closed despite failed flush
|
||||
@@ -1356,6 +1395,28 @@ class CommonBufferedTests:
|
||||
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:
|
||||
|
||||
@@ -1717,20 +1778,6 @@ class BufferedReaderTest(unittest.TestCase, CommonBufferedTests):
|
||||
class CBufferedReaderTest(BufferedReaderTest, SizeofTest):
|
||||
tp = io.BufferedReader
|
||||
|
||||
@unittest.skip("TODO: RUSTPYTHON, fallible allocation")
|
||||
@skip_if_sanitizer(memory=True, address=True, thread=True,
|
||||
reason="sanitizer defaults to crashing "
|
||||
"instead of returning NULL for malloc failure.")
|
||||
def test_constructor(self):
|
||||
BufferedReaderTest.test_constructor(self)
|
||||
# The allocation can succeed on 32-bit builds, e.g. with more
|
||||
# than 2 GiB RAM and a 64-bit kernel.
|
||||
if sys.maxsize > 0x7FFFFFFF:
|
||||
rawio = self.MockRawIO()
|
||||
bufio = self.tp(rawio)
|
||||
self.assertRaises((OverflowError, MemoryError, ValueError),
|
||||
bufio.__init__, rawio, sys.maxsize)
|
||||
|
||||
def test_initialization(self):
|
||||
rawio = self.MockRawIO([b"abc"])
|
||||
bufio = self.tp(rawio)
|
||||
@@ -1748,8 +1795,7 @@ class CBufferedReaderTest(BufferedReaderTest, SizeofTest):
|
||||
# checking this is not so easy.
|
||||
self.assertRaises(OSError, bufio.read, 10)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_garbage_collection(self):
|
||||
# C BufferedReader objects are collected.
|
||||
# The Python version has __del__, so it ends into gc.garbage instead
|
||||
@@ -1766,40 +1812,44 @@ class CBufferedReaderTest(BufferedReaderTest, SizeofTest):
|
||||
def test_args_error(self):
|
||||
# Issue #17275
|
||||
with self.assertRaisesRegex(TypeError, "BufferedReader"):
|
||||
self.tp(io.BytesIO(), 1024, 1024, 1024)
|
||||
self.tp(self.BytesIO(), 1024, 1024, 1024)
|
||||
|
||||
def test_bad_readinto_value(self):
|
||||
rawio = io.BufferedReader(io.BytesIO(b"12"))
|
||||
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__)
|
||||
|
||||
# TODO: RUSTPYTHON, TypeError: 'bytes' object cannot be interpreted as an integer")
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: 'bytes' object cannot be interpreted as an integer")
|
||||
def test_bad_readinto_type(self):
|
||||
rawio = io.BufferedReader(io.BytesIO(b"12"))
|
||||
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)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_flush_error_on_close(self):
|
||||
super().test_flush_error_on_close()
|
||||
return super().test_flush_error_on_close()
|
||||
|
||||
# TODO: RUSTPYTHON, AssertionError: UnsupportedOperation not raised by truncate
|
||||
@unittest.expectedFailure
|
||||
def test_truncate_on_read_only(self): # TODO: RUSTPYTHON, remove when this passes
|
||||
super().test_truncate_on_read_only() # TODO: RUSTPYTHON, remove when this passes
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_seek_character_device_file(self):
|
||||
super().test_seek_character_device_file()
|
||||
return super().test_seek_character_device_file()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: UnsupportedOperation not raised by truncate
|
||||
def test_truncate_on_read_only(self):
|
||||
return super().test_truncate_on_read_only()
|
||||
|
||||
@unittest.skip('TODO: RUSTPYTHON; fallible allocation')
|
||||
def test_constructor(self):
|
||||
return super().test_constructor()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_pickling_subclass(self):
|
||||
return super().test_pickling_subclass()
|
||||
|
||||
|
||||
class PyBufferedReaderTest(BufferedReaderTest):
|
||||
@@ -1909,8 +1959,7 @@ class BufferedWriterTest(unittest.TestCase, CommonBufferedTests):
|
||||
def test_writes_and_truncates(self):
|
||||
self.check_writes(lambda bufio: bufio.truncate(bufio.tell()))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_write_non_blocking(self):
|
||||
raw = self.MockNonBlockWriterIO()
|
||||
bufio = self.tp(raw, 8)
|
||||
@@ -2107,20 +2156,6 @@ class BufferedWriterTest(unittest.TestCase, CommonBufferedTests):
|
||||
class CBufferedWriterTest(BufferedWriterTest, SizeofTest):
|
||||
tp = io.BufferedWriter
|
||||
|
||||
@unittest.skip("TODO: RUSTPYTHON, fallible allocation")
|
||||
@skip_if_sanitizer(memory=True, address=True, thread=True,
|
||||
reason="sanitizer defaults to crashing "
|
||||
"instead of returning NULL for malloc failure.")
|
||||
def test_constructor(self):
|
||||
BufferedWriterTest.test_constructor(self)
|
||||
# The allocation can succeed on 32-bit builds, e.g. with more
|
||||
# than 2 GiB RAM and a 64-bit kernel.
|
||||
if sys.maxsize > 0x7FFFFFFF:
|
||||
rawio = self.MockRawIO()
|
||||
bufio = self.tp(rawio)
|
||||
self.assertRaises((OverflowError, MemoryError, ValueError),
|
||||
bufio.__init__, rawio, sys.maxsize)
|
||||
|
||||
def test_initialization(self):
|
||||
rawio = self.MockRawIO()
|
||||
bufio = self.tp(rawio)
|
||||
@@ -2131,8 +2166,7 @@ class CBufferedWriterTest(BufferedWriterTest, SizeofTest):
|
||||
self.assertRaises(ValueError, bufio.__init__, rawio, buffer_size=-1)
|
||||
self.assertRaises(ValueError, bufio.write, b"def")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_garbage_collection(self):
|
||||
# C BufferedWriter objects are collected, and collecting them flushes
|
||||
# all data to disk.
|
||||
@@ -2155,11 +2189,17 @@ class CBufferedWriterTest(BufferedWriterTest, SizeofTest):
|
||||
with self.assertRaisesRegex(TypeError, "BufferedWriter"):
|
||||
self.tp(self.BytesIO(), 1024, 1024, 1024)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_flush_error_on_close(self):
|
||||
super().test_flush_error_on_close()
|
||||
return super().test_flush_error_on_close()
|
||||
|
||||
@unittest.skip('TODO: RUSTPYTHON; fallible allocation')
|
||||
def test_constructor(self):
|
||||
return super().test_constructor()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_pickling_subclass(self):
|
||||
return super().test_pickling_subclass()
|
||||
|
||||
class PyBufferedWriterTest(BufferedWriterTest):
|
||||
tp = pyio.BufferedWriter
|
||||
@@ -2637,22 +2677,7 @@ class BufferedRandomTest(BufferedReaderTest, BufferedWriterTest):
|
||||
class CBufferedRandomTest(BufferedRandomTest, SizeofTest):
|
||||
tp = io.BufferedRandom
|
||||
|
||||
@unittest.skip("TODO: RUSTPYTHON, fallible allocation")
|
||||
@skip_if_sanitizer(memory=True, address=True, thread=True,
|
||||
reason="sanitizer defaults to crashing "
|
||||
"instead of returning NULL for malloc failure.")
|
||||
def test_constructor(self):
|
||||
BufferedRandomTest.test_constructor(self)
|
||||
# The allocation can succeed on 32-bit builds, e.g. with more
|
||||
# than 2 GiB RAM and a 64-bit kernel.
|
||||
if sys.maxsize > 0x7FFFFFFF:
|
||||
rawio = self.MockRawIO()
|
||||
bufio = self.tp(rawio)
|
||||
self.assertRaises((OverflowError, MemoryError, ValueError),
|
||||
bufio.__init__, rawio, sys.maxsize)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_garbage_collection(self):
|
||||
CBufferedReaderTest.test_garbage_collection(self)
|
||||
CBufferedWriterTest.test_garbage_collection(self)
|
||||
@@ -2662,20 +2687,25 @@ class CBufferedRandomTest(BufferedRandomTest, SizeofTest):
|
||||
with self.assertRaisesRegex(TypeError, "BufferedRandom"):
|
||||
self.tp(self.BytesIO(), 1024, 1024, 1024)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_flush_error_on_close(self):
|
||||
super().test_flush_error_on_close()
|
||||
return super().test_flush_error_on_close()
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_seek_character_device_file(self):
|
||||
super().test_seek_character_device_file()
|
||||
return super().test_seek_character_device_file()
|
||||
|
||||
# TODO: RUSTPYTHON; f.read1(1) returns b'a'
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; f.read1(1) returns b'a'
|
||||
def test_read1_after_write(self):
|
||||
super().test_read1_after_write()
|
||||
return super().test_read1_after_write()
|
||||
|
||||
@unittest.skip('TODO: RUSTPYTHON; fallible allocation')
|
||||
def test_constructor(self):
|
||||
return super().test_constructor()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_pickling_subclass(self):
|
||||
return super().test_pickling_subclass()
|
||||
|
||||
|
||||
class PyBufferedRandomTest(BufferedRandomTest):
|
||||
@@ -2935,11 +2965,16 @@ class TextIOWrapperTest(unittest.TestCase):
|
||||
# Issue #25455
|
||||
raw = self.BytesIO()
|
||||
t = self.TextIOWrapper(raw, encoding="utf-8")
|
||||
with support.swap_attr(raw, 'name', t):
|
||||
try:
|
||||
with support.swap_attr(raw, 'name', t), support.infinite_recursion(25):
|
||||
with self.assertRaises(RuntimeError):
|
||||
repr(t) # Should not crash
|
||||
except RuntimeError:
|
||||
pass
|
||||
|
||||
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()
|
||||
@@ -3166,6 +3201,7 @@ class TextIOWrapperTest(unittest.TestCase):
|
||||
support.gc_collect()
|
||||
self.assertEqual(record, [1, 2, 3])
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_error_through_destructor(self):
|
||||
# Test that the exception state is not modified by a destructor,
|
||||
# even if close() fails.
|
||||
@@ -3174,10 +3210,7 @@ class TextIOWrapperTest(unittest.TestCase):
|
||||
with self.assertRaises(AttributeError):
|
||||
self.TextIOWrapper(rawio, encoding="utf-8").xyzzy
|
||||
|
||||
if not IOBASE_EMITS_UNRAISABLE:
|
||||
self.assertIsNone(cm.unraisable)
|
||||
elif cm.unraisable is not None:
|
||||
self.assertEqual(cm.unraisable.exc_type, OSError)
|
||||
self.assertEqual(cm.unraisable.exc_type, OSError)
|
||||
|
||||
# Systematic tests of the text I/O API
|
||||
|
||||
@@ -3327,8 +3360,7 @@ class TextIOWrapperTest(unittest.TestCase):
|
||||
finally:
|
||||
StatefulIncrementalDecoder.codecEnabled = 0
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_multibyte_seek_and_tell(self):
|
||||
f = self.open(os_helper.TESTFN, "w", encoding="euc_jp")
|
||||
f.write("AB\n\u3046\u3048\n")
|
||||
@@ -3344,8 +3376,7 @@ class TextIOWrapperTest(unittest.TestCase):
|
||||
self.assertEqual(f.tell(), p1)
|
||||
f.close()
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_seek_with_encoder_state(self):
|
||||
f = self.open(os_helper.TESTFN, "w", encoding="euc_jis_2004")
|
||||
f.write("\u00e6\u0300")
|
||||
@@ -3359,8 +3390,7 @@ class TextIOWrapperTest(unittest.TestCase):
|
||||
self.assertEqual(f.readline(), "\u00e6\u0300\u0300")
|
||||
f.close()
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_encoded_writes(self):
|
||||
data = "1234567890"
|
||||
tests = ("utf-16",
|
||||
@@ -3499,8 +3529,7 @@ class TextIOWrapperTest(unittest.TestCase):
|
||||
|
||||
self.assertEqual(buffer.seekable(), txt.seekable())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_append_bom(self):
|
||||
# The BOM is not written again when appending to a non-empty file
|
||||
filename = os_helper.TESTFN
|
||||
@@ -3516,8 +3545,7 @@ class TextIOWrapperTest(unittest.TestCase):
|
||||
with self.open(filename, 'rb') as f:
|
||||
self.assertEqual(f.read(), 'aaaxxx'.encode(charset))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_seek_bom(self):
|
||||
# Same test, but when seeking manually
|
||||
filename = os_helper.TESTFN
|
||||
@@ -3533,8 +3561,7 @@ class TextIOWrapperTest(unittest.TestCase):
|
||||
with self.open(filename, 'rb') as f:
|
||||
self.assertEqual(f.read(), 'bbbzzz'.encode(charset))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_seek_append_bom(self):
|
||||
# Same test, but first seek to the start and then to the end
|
||||
filename = os_helper.TESTFN
|
||||
@@ -3795,17 +3822,14 @@ class TextIOWrapperTest(unittest.TestCase):
|
||||
codecs.lookup('utf-8')
|
||||
|
||||
class C:
|
||||
def __init__(self):
|
||||
self.buf = io.BytesIO()
|
||||
def __del__(self):
|
||||
io.TextIOWrapper(self.buf, **{kwargs})
|
||||
io.TextIOWrapper(io.BytesIO(), **{kwargs})
|
||||
print("ok")
|
||||
c = C()
|
||||
""".format(iomod=iomod, kwargs=kwargs)
|
||||
return assert_python_ok("-c", code)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_create_at_shutdown_without_encoding(self):
|
||||
rc, out, err = self._check_create_at_shutdown()
|
||||
if err:
|
||||
@@ -3815,8 +3839,7 @@ class TextIOWrapperTest(unittest.TestCase):
|
||||
else:
|
||||
self.assertEqual("ok", out.decode().strip())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_create_at_shutdown_with_encoding(self):
|
||||
rc, out, err = self._check_create_at_shutdown(encoding='utf-8',
|
||||
errors='strict')
|
||||
@@ -4042,6 +4065,28 @@ class TextIOWrapperTest(unittest.TestCase):
|
||||
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
|
||||
|
||||
|
||||
class MemviewBytesIO(io.BytesIO):
|
||||
'''A BytesIO object whose read method returns memoryviews
|
||||
@@ -4066,98 +4111,7 @@ class CTextIOWrapperTest(TextIOWrapperTest):
|
||||
io = io
|
||||
shutdown_error = "LookupError: unknown encoding: ascii"
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_constructor(self):
|
||||
super().test_constructor()
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_detach(self):
|
||||
super().test_detach()
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_reconfigure_encoding_read(self):
|
||||
super().test_reconfigure_encoding_read()
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_reconfigure_line_buffering(self):
|
||||
super().test_reconfigure_line_buffering()
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_basic_io(self):
|
||||
super().test_basic_io()
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_telling(self):
|
||||
super().test_telling()
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_uninitialized(self):
|
||||
super().test_uninitialized()
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_non_text_encoding_codecs_are_rejected(self):
|
||||
super().test_non_text_encoding_codecs_are_rejected()
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_repr(self):
|
||||
super().test_repr()
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_newlines(self):
|
||||
super().test_newlines()
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_newlines_input(self):
|
||||
super().test_newlines_input()
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_reconfigure_write_through(self):
|
||||
super().test_reconfigure_write_through()
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_reconfigure_write_fromascii(self):
|
||||
super().test_reconfigure_write_fromascii()
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_reconfigure_write(self):
|
||||
super().test_reconfigure_write()
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_reconfigure_defaults(self):
|
||||
super().test_reconfigure_defaults()
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_reconfigure_newline(self):
|
||||
super().test_reconfigure_newline()
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_reconfigure_errors(self):
|
||||
super().test_reconfigure_errors()
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_reconfigure_locale(self):
|
||||
super().test_reconfigure_locale()
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_initialization(self):
|
||||
r = self.BytesIO(b"\xc3\xa9\n\n")
|
||||
b = self.BufferedReader(r, 1000)
|
||||
@@ -4168,8 +4122,7 @@ class CTextIOWrapperTest(TextIOWrapperTest):
|
||||
t = self.TextIOWrapper.__new__(self.TextIOWrapper)
|
||||
self.assertRaises(Exception, repr, t)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_garbage_collection(self):
|
||||
# C TextIOWrapper objects are collected, and collecting them flushes
|
||||
# all data to disk.
|
||||
@@ -4233,20 +4186,121 @@ class CTextIOWrapperTest(TextIOWrapperTest):
|
||||
t.write("x"*chunk_size)
|
||||
self.assertEqual([b"abcdef", b"ghi", b"x"*chunk_size], buf._write_stack)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
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)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_basic_io(self):
|
||||
return super().test_basic_io()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_constructor(self):
|
||||
return super().test_constructor()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_detach(self):
|
||||
return super().test_detach()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_newlines(self):
|
||||
return super().test_newlines()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_newlines_input(self):
|
||||
return super().test_newlines_input()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_non_text_encoding_codecs_are_rejected(self):
|
||||
return super().test_non_text_encoding_codecs_are_rejected()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_reconfigure_defaults(self):
|
||||
return super().test_reconfigure_defaults()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_reconfigure_encoding_read(self):
|
||||
return super().test_reconfigure_encoding_read()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_reconfigure_errors(self):
|
||||
return super().test_reconfigure_errors()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_reconfigure_line_buffering(self):
|
||||
return super().test_reconfigure_line_buffering()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_reconfigure_locale(self):
|
||||
return super().test_reconfigure_locale()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_reconfigure_newline(self):
|
||||
return super().test_reconfigure_newline()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_reconfigure_write(self):
|
||||
return super().test_reconfigure_write()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_reconfigure_write_fromascii(self):
|
||||
return super().test_reconfigure_write_fromascii()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_reconfigure_write_through(self):
|
||||
return super().test_reconfigure_write_through()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_repr(self):
|
||||
return super().test_repr()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_telling(self):
|
||||
return super().test_telling()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_uninitialized(self):
|
||||
return super().test_uninitialized()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_recursive_repr(self):
|
||||
return super().test_recursive_repr()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_pickling_subclass(self):
|
||||
return super().test_pickling_subclass()
|
||||
|
||||
|
||||
class PyTextIOWrapperTest(TextIOWrapperTest):
|
||||
io = pyio
|
||||
shutdown_error = "LookupError: unknown encoding: ascii"
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: ValueError not raised
|
||||
def test_constructor(self):
|
||||
super().test_constructor()
|
||||
return super().test_constructor()
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_newlines(self):
|
||||
super().test_newlines()
|
||||
return super().test_newlines()
|
||||
|
||||
|
||||
class IncrementalNewlineDecoderTest(unittest.TestCase):
|
||||
@@ -4326,8 +4380,7 @@ class IncrementalNewlineDecoderTest(unittest.TestCase):
|
||||
self.assertEqual(decoder.decode(input), "abc")
|
||||
self.assertEqual(decoder.newlines, None)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_newline_decoder(self):
|
||||
encodings = (
|
||||
# None meaning the IncrementalNewlineDecoder takes unicode input
|
||||
@@ -4489,8 +4542,7 @@ class MiscIOTest(unittest.TestCase):
|
||||
self.assertRaises(ValueError, f.writelines, [])
|
||||
self.assertRaises(ValueError, next, f)
|
||||
|
||||
# TODO: RUSTPYTHON, cyclic gc
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; cyclic gc
|
||||
def test_blockingioerror(self):
|
||||
# Various BlockingIOError issues
|
||||
class C(str):
|
||||
@@ -4538,15 +4590,14 @@ class MiscIOTest(unittest.TestCase):
|
||||
self._check_abc_inheritance(io)
|
||||
|
||||
def _check_warn_on_dealloc(self, *args, **kwargs):
|
||||
f = open(*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]))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
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")
|
||||
@@ -4569,10 +4620,9 @@ class MiscIOTest(unittest.TestCase):
|
||||
r, w = os.pipe()
|
||||
fds += r, w
|
||||
with warnings_helper.check_no_resource_warning(self):
|
||||
open(r, *args, closefd=False, **kwargs)
|
||||
self.open(r, *args, closefd=False, **kwargs)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()")
|
||||
def test_warn_on_dealloc_fd(self):
|
||||
self._check_warn_on_dealloc_fd("rb", buffering=0)
|
||||
@@ -4602,16 +4652,14 @@ class MiscIOTest(unittest.TestCase):
|
||||
with self.assertRaisesRegex(TypeError, msg):
|
||||
pickle.dumps(f, protocol)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.skipIf(
|
||||
support.is_emscripten, "fstat() of a pipe fd is not supported"
|
||||
)
|
||||
def test_nonblock_pipe_write_bigbuf(self):
|
||||
self._test_nonblock_pipe_write(16*1024)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.skipIf(
|
||||
support.is_emscripten, "fstat() of a pipe fd is not supported"
|
||||
)
|
||||
@@ -4733,8 +4781,7 @@ class MiscIOTest(unittest.TestCase):
|
||||
proc = assert_python_failure('-X', 'dev', '-c', code)
|
||||
self.assertEqual(proc.rc, 10, proc)
|
||||
|
||||
# TODO: RUSTPYTHON, AssertionError: 0 != 2
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: 0 != 2
|
||||
def test_check_encoding_warning(self):
|
||||
# PEP 597: Raise warning when encoding is not specified
|
||||
# and sys.flags.warn_default_encoding is set.
|
||||
@@ -4758,8 +4805,7 @@ class MiscIOTest(unittest.TestCase):
|
||||
self.assertTrue(
|
||||
warnings[1].startswith(b"<string>:8: EncodingWarning: "))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_text_encoding(self):
|
||||
# PEP 597, bpo-47000. io.text_encoding() returns "locale" or "utf-8"
|
||||
# based on sys.flags.utf8_mode
|
||||
@@ -4837,10 +4883,9 @@ class CMiscIOTest(MiscIOTest):
|
||||
def test_daemon_threads_shutdown_stderr_deadlock(self):
|
||||
self.check_daemon_threads_shutdown_deadlock('stderr')
|
||||
|
||||
# TODO: RUSTPYTHON, AssertionError: 22 != 10 : _PythonRunResult(rc=22, out=b'', err=b'')
|
||||
@unittest.expectedFailure
|
||||
def test_check_encoding_errors(self): # TODO: RUSTPYTHON, remove when this passes
|
||||
super().test_check_encoding_errors() # TODO: RUSTPYTHON, remove when this passes
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: 22 != 10 : _PythonRunResult(rc=22, out=b'', err=b'')
|
||||
def test_check_encoding_errors(self):
|
||||
return super().test_check_encoding_errors()
|
||||
|
||||
|
||||
class PyMiscIOTest(MiscIOTest):
|
||||
@@ -5014,16 +5059,14 @@ class SignalsTest(unittest.TestCase):
|
||||
os.close(w)
|
||||
os.close(r)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@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")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@requires_alarm
|
||||
@support.requires_resource('walltime')
|
||||
def test_interrupted_read_retry_text(self):
|
||||
@@ -5098,15 +5141,13 @@ class SignalsTest(unittest.TestCase):
|
||||
if e.errno != errno.EBADF:
|
||||
raise
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@requires_alarm
|
||||
@support.requires_resource('walltime')
|
||||
def test_interrupted_write_retry_buffered(self):
|
||||
self.check_interrupted_write_retry(b"x", mode="wb")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@requires_alarm
|
||||
@support.requires_resource('walltime')
|
||||
def test_interrupted_write_retry_text(self):
|
||||
|
||||
18
Lib/test/test_raise.py
vendored
18
Lib/test/test_raise.py
vendored
@@ -185,6 +185,21 @@ class TestCause(unittest.TestCase):
|
||||
else:
|
||||
self.fail("No exception raised")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: 'classmethod' object is not callable
|
||||
def test_class_cause_nonexception_result(self):
|
||||
class ConstructsNone(BaseException):
|
||||
@classmethod
|
||||
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")
|
||||
|
||||
def test_instance_cause(self):
|
||||
cause = KeyError()
|
||||
try:
|
||||
@@ -233,8 +248,7 @@ class TestTracebackType(unittest.TestCase):
|
||||
def raiser(self):
|
||||
raise ValueError
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_attrs(self):
|
||||
try:
|
||||
self.raiser()
|
||||
|
||||
80
Lib/test/test_scope.py
vendored
80
Lib/test/test_scope.py
vendored
@@ -177,6 +177,57 @@ class ScopeTests(unittest.TestCase):
|
||||
self.assertEqual(foo(a=42), 50)
|
||||
self.assertEqual(foo(), 25)
|
||||
|
||||
def testCellIsArgAndEscapes(self):
|
||||
# We need to be sure that a cell passed in as an arg still
|
||||
# gets wrapped in a new cell if the arg escapes into an
|
||||
# inner function (closure).
|
||||
|
||||
def external():
|
||||
value = 42
|
||||
def inner():
|
||||
return value
|
||||
cell, = inner.__closure__
|
||||
return cell
|
||||
cell_ext = external()
|
||||
|
||||
def spam(arg):
|
||||
def eggs():
|
||||
return arg
|
||||
return eggs
|
||||
|
||||
eggs = spam(cell_ext)
|
||||
cell_closure, = eggs.__closure__
|
||||
cell_eggs = eggs()
|
||||
|
||||
self.assertIs(cell_eggs, cell_ext)
|
||||
self.assertIsNot(cell_eggs, cell_closure)
|
||||
|
||||
def testCellIsLocalAndEscapes(self):
|
||||
# We need to be sure that a cell bound to a local still
|
||||
# gets wrapped in a new cell if the local escapes into an
|
||||
# inner function (closure).
|
||||
|
||||
def external():
|
||||
value = 42
|
||||
def inner():
|
||||
return value
|
||||
cell, = inner.__closure__
|
||||
return cell
|
||||
cell_ext = external()
|
||||
|
||||
def spam(arg):
|
||||
cell = arg
|
||||
def eggs():
|
||||
return cell
|
||||
return eggs
|
||||
|
||||
eggs = spam(cell_ext)
|
||||
cell_closure, = eggs.__closure__
|
||||
cell_eggs = eggs()
|
||||
|
||||
self.assertIs(cell_eggs, cell_ext)
|
||||
self.assertIsNot(cell_eggs, cell_closure)
|
||||
|
||||
def testRecursion(self):
|
||||
|
||||
def f(x):
|
||||
@@ -641,10 +692,7 @@ class ScopeTests(unittest.TestCase):
|
||||
self.assertEqual(c.dec(), 1)
|
||||
self.assertEqual(c.dec(), 0)
|
||||
|
||||
# TODO: RUSTPYTHON, figure out how to communicate that `y = 9` should be
|
||||
# stored as a global rather than a STORE_NAME, even when
|
||||
# the `global y` is in a nested subscope
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; figure out how to communicate that `y = 9` should be stored as a global rather than a STORE_NAME, even when the `global y` is in a nested subscope
|
||||
def testGlobalInParallelNestedFunctions(self):
|
||||
# A symbol table bug leaked the global statement from one
|
||||
# function to other nested functions in the same block.
|
||||
@@ -763,6 +811,30 @@ class ScopeTests(unittest.TestCase):
|
||||
gc_collect() # For PyPy or other GCs.
|
||||
self.assertIsNone(ref())
|
||||
|
||||
def test_multiple_nesting(self):
|
||||
# Regression test for https://github.com/python/cpython/issues/121863
|
||||
class MultiplyNested:
|
||||
def f1(self):
|
||||
__arg = 1
|
||||
class D:
|
||||
def g(self, __arg):
|
||||
return __arg
|
||||
return D().g(_MultiplyNested__arg=2)
|
||||
|
||||
def f2(self):
|
||||
__arg = 1
|
||||
class D:
|
||||
def g(self, __arg):
|
||||
return __arg
|
||||
return D().g
|
||||
|
||||
inst = MultiplyNested()
|
||||
with self.assertRaises(TypeError):
|
||||
inst.f1()
|
||||
|
||||
closure = inst.f2()
|
||||
with self.assertRaises(TypeError):
|
||||
closure(_MultiplyNested__arg=2)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
3
Lib/test/test_shutil.py
vendored
3
Lib/test/test_shutil.py
vendored
@@ -196,6 +196,7 @@ class TestRmTree(BaseTest, unittest.TestCase):
|
||||
self.assertIsInstance(victim, bytes)
|
||||
shutil.rmtree(victim)
|
||||
|
||||
@unittest.skipIf(sys.platform == 'win32', 'TODO: RUSTPYTHON; flaky')
|
||||
@os_helper.skip_unless_symlink
|
||||
def test_rmtree_fails_on_symlink_onerror(self):
|
||||
tmp = self.mkdtemp()
|
||||
@@ -1477,7 +1478,7 @@ class TestCopy(BaseTest, unittest.TestCase):
|
||||
finally:
|
||||
shutil.rmtree(TESTFN, ignore_errors=True)
|
||||
|
||||
@unittest.expectedFailureIfWindows('TODO: RUSTPYTHON')
|
||||
@unittest.expectedFailureIfWindows('TODO: RUSTPYTHON; AssertionError: SameFileError not raised for copyfile')
|
||||
@os_helper.skip_unless_symlink
|
||||
def test_dont_copy_file_onto_symlink_to_itself(self):
|
||||
# bug 851123.
|
||||
|
||||
3
Lib/test/test_slice.py
vendored
3
Lib/test/test_slice.py
vendored
@@ -286,8 +286,7 @@ class SliceTest(unittest.TestCase):
|
||||
self.assertIsNot(s.stop, c.stop)
|
||||
self.assertIsNot(s.step, c.step)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_cycle(self):
|
||||
class myobj(): pass
|
||||
o = myobj()
|
||||
|
||||
15
Lib/test/test_string_literals.py
vendored
15
Lib/test/test_string_literals.py
vendored
@@ -105,8 +105,7 @@ class TestLiterals(unittest.TestCase):
|
||||
self.assertRaises(SyntaxError, eval, r""" '\U000000' """)
|
||||
self.assertRaises(SyntaxError, eval, r""" '\U0000000' """)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_eval_str_invalid_escape(self):
|
||||
for b in range(1, 128):
|
||||
if b in b"""\n\r"'01234567NU\\abfnrtuvx""":
|
||||
@@ -145,8 +144,7 @@ class TestLiterals(unittest.TestCase):
|
||||
self.assertRegex(str(w[0].message), 'invalid escape sequence')
|
||||
self.assertEqual(w[0].filename, '<string>')
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_eval_str_invalid_octal_escape(self):
|
||||
for i in range(0o400, 0o1000):
|
||||
with self.assertWarns(SyntaxWarning):
|
||||
@@ -172,8 +170,7 @@ class TestLiterals(unittest.TestCase):
|
||||
self.assertEqual(exc.lineno, 2)
|
||||
self.assertEqual(exc.offset, 1)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_invalid_escape_locations_with_offset(self):
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter('error', category=SyntaxWarning)
|
||||
@@ -223,8 +220,7 @@ class TestLiterals(unittest.TestCase):
|
||||
self.assertRaises(SyntaxError, eval, r""" b'\x' """)
|
||||
self.assertRaises(SyntaxError, eval, r""" b'\x0' """)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_eval_bytes_invalid_escape(self):
|
||||
for b in range(1, 128):
|
||||
if b in b"""\n\r"'01234567\\abfnrtvx""":
|
||||
@@ -250,8 +246,7 @@ class TestLiterals(unittest.TestCase):
|
||||
self.assertEqual(exc.filename, '<string>')
|
||||
self.assertEqual(exc.lineno, 2)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_eval_bytes_invalid_octal_escape(self):
|
||||
for i in range(0o400, 0o1000):
|
||||
with self.assertWarns(SyntaxWarning):
|
||||
|
||||
17
Lib/test/test_strtod.py
vendored
17
Lib/test/test_strtod.py
vendored
@@ -146,7 +146,7 @@ class StrtodTests(unittest.TestCase):
|
||||
digits *= 5
|
||||
exponent -= 1
|
||||
|
||||
@unittest.skip("TODO: RUSTPYTHON, fails on debug mode, flaky in release mode")
|
||||
@unittest.skip('TODO: RUSTPYTHON; fails on debug mode, flaky in release mode')
|
||||
def test_halfway_cases(self):
|
||||
# test halfway cases for the round-half-to-even rule
|
||||
for i in range(100 * TEST_SIZE):
|
||||
@@ -173,8 +173,7 @@ class StrtodTests(unittest.TestCase):
|
||||
s = '{}e{}'.format(digits, exponent)
|
||||
self.check_strtod(s)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_boundaries(self):
|
||||
# boundaries expressed as triples (n, e, u), where
|
||||
# n*10**e is an approximation to the boundary value and
|
||||
@@ -195,8 +194,7 @@ class StrtodTests(unittest.TestCase):
|
||||
u *= 10
|
||||
e -= 1
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_underflow_boundary(self):
|
||||
# test values close to 2**-1075, the underflow boundary; similar
|
||||
# to boundary_tests, except that the random error doesn't scale
|
||||
@@ -208,8 +206,7 @@ class StrtodTests(unittest.TestCase):
|
||||
s = '{}e{}'.format(digits, exponent)
|
||||
self.check_strtod(s)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_bigcomp(self):
|
||||
for ndigs in 5, 10, 14, 15, 16, 17, 18, 19, 20, 40, 41, 50:
|
||||
dig10 = 10**ndigs
|
||||
@@ -219,8 +216,7 @@ class StrtodTests(unittest.TestCase):
|
||||
s = '{}e{}'.format(digits, exponent)
|
||||
self.check_strtod(s)
|
||||
|
||||
# TODO: RUSTPYTHON, Incorrectly rounded str->float conversion for -07e-321
|
||||
@unittest.skip("TODO: RUSTPYTHON; flaky test")
|
||||
@unittest.skip('TODO: RUSTPYTHON; flaky test')
|
||||
def test_parsing(self):
|
||||
# make '0' more likely to be chosen than other digits
|
||||
digits = '000000123456789'
|
||||
@@ -288,8 +284,7 @@ class StrtodTests(unittest.TestCase):
|
||||
self.assertEqual(float(negative_exp(20000)), 1.0)
|
||||
self.assertEqual(float(negative_exp(30000)), 1.0)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_particular(self):
|
||||
# inputs that produced crashes or incorrectly rounded results with
|
||||
# previous versions of dtoa.c, for various reasons
|
||||
|
||||
29
Lib/test/test_sundry.py
vendored
29
Lib/test/test_sundry.py
vendored
@@ -1,14 +1,11 @@
|
||||
"""Do a minimal test of all the modules that aren't otherwise tested."""
|
||||
import importlib
|
||||
import platform
|
||||
import sys
|
||||
from test import support
|
||||
from test.support import import_helper
|
||||
from test.support import warnings_helper
|
||||
import unittest
|
||||
|
||||
class TestUntestedModules(unittest.TestCase):
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
|
||||
def test_untested_modules_can_be_imported(self):
|
||||
untested = ('encodings',)
|
||||
with warnings_helper.check_warnings(quiet=True):
|
||||
@@ -21,31 +18,6 @@ class TestUntestedModules(unittest.TestCase):
|
||||
self.fail('{} has tests even though test_sundry claims '
|
||||
'otherwise'.format(name))
|
||||
|
||||
import distutils.bcppcompiler
|
||||
import distutils.ccompiler
|
||||
import distutils.cygwinccompiler
|
||||
import distutils.filelist
|
||||
import distutils.text_file
|
||||
import distutils.unixccompiler
|
||||
|
||||
import distutils.command.bdist_dumb
|
||||
if sys.platform.startswith('win') and not platform.win32_is_iot():
|
||||
import distutils.command.bdist_msi
|
||||
import distutils.command.bdist
|
||||
import distutils.command.bdist_rpm
|
||||
import distutils.command.build_clib
|
||||
import distutils.command.build_ext
|
||||
import distutils.command.build
|
||||
import distutils.command.clean
|
||||
import distutils.command.config
|
||||
import distutils.command.install_data
|
||||
import distutils.command.install_egg_info
|
||||
import distutils.command.install_headers
|
||||
import distutils.command.install_lib
|
||||
import distutils.command.register
|
||||
import distutils.command.sdist
|
||||
import distutils.command.upload
|
||||
|
||||
import html.entities
|
||||
|
||||
try:
|
||||
@@ -54,5 +26,6 @@ class TestUntestedModules(unittest.TestCase):
|
||||
if support.verbose:
|
||||
print("skipping tty")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
31
Lib/test/test_uuid.py
vendored
31
Lib/test/test_uuid.py
vendored
@@ -1,6 +1,7 @@
|
||||
import unittest
|
||||
from test import support
|
||||
from test.support import import_helper
|
||||
from test.support.script_helper import assert_python_ok
|
||||
import builtins
|
||||
import contextlib
|
||||
import copy
|
||||
@@ -32,8 +33,7 @@ def mock_get_command_stdout(data):
|
||||
class BaseTestUUID:
|
||||
uuid = None
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_safe_uuid_enum(self):
|
||||
class CheckedSafeUUID(enum.Enum):
|
||||
safe = 0
|
||||
@@ -775,10 +775,37 @@ class BaseTestUUID:
|
||||
class TestUUIDWithoutExtModule(BaseTestUUID, unittest.TestCase):
|
||||
uuid = py_uuid
|
||||
|
||||
|
||||
@unittest.skipUnless(c_uuid, 'requires the C _uuid module')
|
||||
class TestUUIDWithExtModule(BaseTestUUID, unittest.TestCase):
|
||||
uuid = c_uuid
|
||||
|
||||
def check_has_stable_libuuid_extractable_node(self):
|
||||
if not self.uuid._has_stable_extractable_node:
|
||||
self.skipTest("libuuid cannot deduce MAC address")
|
||||
|
||||
@unittest.skipUnless(os.name == 'posix', 'POSIX only')
|
||||
def test_unix_getnode_from_libuuid(self):
|
||||
self.check_has_stable_libuuid_extractable_node()
|
||||
script = 'import uuid; print(uuid._unix_getnode())'
|
||||
_, n_a, _ = assert_python_ok('-c', script)
|
||||
_, n_b, _ = assert_python_ok('-c', script)
|
||||
n_a, n_b = n_a.decode().strip(), n_b.decode().strip()
|
||||
self.assertTrue(n_a.isdigit())
|
||||
self.assertTrue(n_b.isdigit())
|
||||
self.assertEqual(n_a, n_b)
|
||||
|
||||
@unittest.skipUnless(os.name == 'nt', 'Windows only')
|
||||
def test_windows_getnode_from_libuuid(self):
|
||||
self.check_has_stable_libuuid_extractable_node()
|
||||
script = 'import uuid; print(uuid._windll_getnode())'
|
||||
_, n_a, _ = assert_python_ok('-c', script)
|
||||
_, n_b, _ = assert_python_ok('-c', script)
|
||||
n_a, n_b = n_a.decode().strip(), n_b.decode().strip()
|
||||
self.assertTrue(n_a.isdigit())
|
||||
self.assertTrue(n_b.isdigit())
|
||||
self.assertEqual(n_a, n_b)
|
||||
|
||||
|
||||
class BaseTestInternals:
|
||||
_uuid = py_uuid
|
||||
|
||||
552
Lib/test/test_yield_from.py
vendored
552
Lib/test/test_yield_from.py
vendored
@@ -538,8 +538,7 @@ class TestPEP380Operation(unittest.TestCase):
|
||||
"finishing g",
|
||||
])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_broken_getattr_handling(self):
|
||||
"""
|
||||
Test subiterator with a broken getattr implementation
|
||||
@@ -787,8 +786,7 @@ class TestPEP380Operation(unittest.TestCase):
|
||||
repr(value),
|
||||
])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_throwing_GeneratorExit_into_subgen_that_returns(self):
|
||||
"""
|
||||
Test throwing GeneratorExit into a subgenerator that
|
||||
@@ -819,8 +817,7 @@ class TestPEP380Operation(unittest.TestCase):
|
||||
"Enter f",
|
||||
])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_throwing_GeneratorExit_into_subgenerator_that_yields(self):
|
||||
"""
|
||||
Test throwing GeneratorExit into a subgenerator that
|
||||
@@ -887,8 +884,7 @@ class TestPEP380Operation(unittest.TestCase):
|
||||
yield from ()
|
||||
self.assertRaises(StopIteration, next, g())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_delegating_generators_claim_to_be_running(self):
|
||||
# Check with basic iteration
|
||||
def one():
|
||||
@@ -904,6 +900,7 @@ class TestPEP380Operation(unittest.TestCase):
|
||||
yield 2
|
||||
g1 = one()
|
||||
self.assertEqual(list(g1), [0, 1, 2, 3])
|
||||
|
||||
# Check with send
|
||||
g1 = one()
|
||||
res = [next(g1)]
|
||||
@@ -913,6 +910,9 @@ class TestPEP380Operation(unittest.TestCase):
|
||||
except StopIteration:
|
||||
pass
|
||||
self.assertEqual(res, [0, 1, 2, 3])
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: Lists differ: [0, 1, 2] != [0, 1, 2, 3]
|
||||
def test_delegating_generators_claim_to_be_running_with_throw(self):
|
||||
# Check with throw
|
||||
class MyErr(Exception):
|
||||
pass
|
||||
@@ -949,8 +949,10 @@ class TestPEP380Operation(unittest.TestCase):
|
||||
except:
|
||||
self.assertEqual(res, [0, 1, 2, 3])
|
||||
raise
|
||||
|
||||
def test_delegating_generators_claim_to_be_running_with_close(self):
|
||||
# Check with close
|
||||
class MyIt(object):
|
||||
class MyIt:
|
||||
def __iter__(self):
|
||||
return self
|
||||
def __next__(self):
|
||||
@@ -1057,6 +1059,538 @@ class TestPEP380Operation(unittest.TestCase):
|
||||
g.send((1, 2, 3, 4))
|
||||
self.assertEqual(v, (1, 2, 3, 4))
|
||||
|
||||
class TestInterestingEdgeCases(unittest.TestCase):
|
||||
|
||||
def assert_stop_iteration(self, iterator):
|
||||
with self.assertRaises(StopIteration) as caught:
|
||||
next(iterator)
|
||||
self.assertIsNone(caught.exception.value)
|
||||
self.assertIsNone(caught.exception.__context__)
|
||||
|
||||
def assert_generator_raised_stop_iteration(self):
|
||||
return self.assertRaisesRegex(RuntimeError, r"^generator raised StopIteration$")
|
||||
|
||||
def assert_generator_ignored_generator_exit(self):
|
||||
return self.assertRaisesRegex(RuntimeError, r"^generator ignored GeneratorExit$")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_close_and_throw_work(self):
|
||||
|
||||
yielded_first = object()
|
||||
yielded_second = object()
|
||||
returned = object()
|
||||
|
||||
def inner():
|
||||
yield yielded_first
|
||||
yield yielded_second
|
||||
return returned
|
||||
|
||||
def outer():
|
||||
return (yield from inner())
|
||||
|
||||
with self.subTest("close"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
g.close()
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
with self.subTest("throw GeneratorExit"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
thrown = GeneratorExit()
|
||||
with self.assertRaises(GeneratorExit) as caught:
|
||||
g.throw(thrown)
|
||||
self.assertIs(caught.exception, thrown)
|
||||
self.assertIsNone(caught.exception.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
with self.subTest("throw StopIteration"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
thrown = StopIteration()
|
||||
# PEP 479:
|
||||
with self.assert_generator_raised_stop_iteration() as caught:
|
||||
g.throw(thrown)
|
||||
self.assertIs(caught.exception.__context__, thrown)
|
||||
self.assertIsNone(caught.exception.__context__.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
with self.subTest("throw BaseException"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
thrown = BaseException()
|
||||
with self.assertRaises(BaseException) as caught:
|
||||
g.throw(thrown)
|
||||
self.assertIs(caught.exception, thrown)
|
||||
self.assertIsNone(caught.exception.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
with self.subTest("throw Exception"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
thrown = Exception()
|
||||
with self.assertRaises(Exception) as caught:
|
||||
g.throw(thrown)
|
||||
self.assertIs(caught.exception, thrown)
|
||||
self.assertIsNone(caught.exception.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: GeneratorExit() is not GeneratorExit()
|
||||
def test_close_and_throw_raise_generator_exit(self):
|
||||
|
||||
yielded_first = object()
|
||||
yielded_second = object()
|
||||
returned = object()
|
||||
|
||||
def inner():
|
||||
try:
|
||||
yield yielded_first
|
||||
yield yielded_second
|
||||
return returned
|
||||
finally:
|
||||
raise raised
|
||||
|
||||
def outer():
|
||||
return (yield from inner())
|
||||
|
||||
with self.subTest("close"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
raised = GeneratorExit()
|
||||
# GeneratorExit is suppressed. This is consistent with PEP 342:
|
||||
# https://peps.python.org/pep-0342/#new-generator-method-close
|
||||
g.close()
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
with self.subTest("throw GeneratorExit"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
raised = GeneratorExit()
|
||||
thrown = GeneratorExit()
|
||||
with self.assertRaises(GeneratorExit) as caught:
|
||||
g.throw(thrown)
|
||||
# The raised GeneratorExit is suppressed, but the thrown one
|
||||
# propagates. This is consistent with PEP 380:
|
||||
# https://peps.python.org/pep-0380/#proposal
|
||||
self.assertIs(caught.exception, thrown)
|
||||
self.assertIsNone(caught.exception.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
with self.subTest("throw StopIteration"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
raised = GeneratorExit()
|
||||
thrown = StopIteration()
|
||||
with self.assertRaises(GeneratorExit) as caught:
|
||||
g.throw(thrown)
|
||||
self.assertIs(caught.exception, raised)
|
||||
self.assertIs(caught.exception.__context__, thrown)
|
||||
self.assertIsNone(caught.exception.__context__.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
with self.subTest("throw BaseException"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
raised = GeneratorExit()
|
||||
thrown = BaseException()
|
||||
with self.assertRaises(GeneratorExit) as caught:
|
||||
g.throw(thrown)
|
||||
self.assertIs(caught.exception, raised)
|
||||
self.assertIs(caught.exception.__context__, thrown)
|
||||
self.assertIsNone(caught.exception.__context__.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
with self.subTest("throw Exception"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
raised = GeneratorExit()
|
||||
thrown = Exception()
|
||||
with self.assertRaises(GeneratorExit) as caught:
|
||||
g.throw(thrown)
|
||||
self.assertIs(caught.exception, raised)
|
||||
self.assertIs(caught.exception.__context__, thrown)
|
||||
self.assertIsNone(caught.exception.__context__.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeError not raised
|
||||
def test_close_and_throw_raise_stop_iteration(self):
|
||||
|
||||
yielded_first = object()
|
||||
yielded_second = object()
|
||||
returned = object()
|
||||
|
||||
def inner():
|
||||
try:
|
||||
yield yielded_first
|
||||
yield yielded_second
|
||||
return returned
|
||||
finally:
|
||||
raise raised
|
||||
|
||||
def outer():
|
||||
return (yield from inner())
|
||||
|
||||
with self.subTest("close"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
raised = StopIteration()
|
||||
# PEP 479:
|
||||
with self.assert_generator_raised_stop_iteration() as caught:
|
||||
g.close()
|
||||
self.assertIs(caught.exception.__context__, raised)
|
||||
self.assertIsInstance(caught.exception.__context__.__context__, GeneratorExit)
|
||||
self.assertIsNone(caught.exception.__context__.__context__.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
with self.subTest("throw GeneratorExit"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
raised = StopIteration()
|
||||
thrown = GeneratorExit()
|
||||
# PEP 479:
|
||||
with self.assert_generator_raised_stop_iteration() as caught:
|
||||
g.throw(thrown)
|
||||
self.assertIs(caught.exception.__context__, raised)
|
||||
# This isn't the same GeneratorExit as thrown! It's the one created
|
||||
# by calling inner.close():
|
||||
self.assertIsInstance(caught.exception.__context__.__context__, GeneratorExit)
|
||||
self.assertIsNone(caught.exception.__context__.__context__.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
with self.subTest("throw StopIteration"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
raised = StopIteration()
|
||||
thrown = StopIteration()
|
||||
# PEP 479:
|
||||
with self.assert_generator_raised_stop_iteration() as caught:
|
||||
g.throw(thrown)
|
||||
self.assertIs(caught.exception.__context__, raised)
|
||||
self.assertIs(caught.exception.__context__.__context__, thrown)
|
||||
self.assertIsNone(caught.exception.__context__.__context__.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
with self.subTest("throw BaseException"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
raised = StopIteration()
|
||||
thrown = BaseException()
|
||||
# PEP 479:
|
||||
with self.assert_generator_raised_stop_iteration() as caught:
|
||||
g.throw(thrown)
|
||||
self.assertIs(caught.exception.__context__, raised)
|
||||
self.assertIs(caught.exception.__context__.__context__, thrown)
|
||||
self.assertIsNone(caught.exception.__context__.__context__.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
with self.subTest("throw Exception"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
raised = StopIteration()
|
||||
thrown = Exception()
|
||||
# PEP 479:
|
||||
with self.assert_generator_raised_stop_iteration() as caught:
|
||||
g.throw(thrown)
|
||||
self.assertIs(caught.exception.__context__, raised)
|
||||
self.assertIs(caught.exception.__context__.__context__, thrown)
|
||||
self.assertIsNone(caught.exception.__context__.__context__.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
def test_close_and_throw_raise_base_exception(self):
|
||||
|
||||
yielded_first = object()
|
||||
yielded_second = object()
|
||||
returned = object()
|
||||
|
||||
def inner():
|
||||
try:
|
||||
yield yielded_first
|
||||
yield yielded_second
|
||||
return returned
|
||||
finally:
|
||||
raise raised
|
||||
|
||||
def outer():
|
||||
return (yield from inner())
|
||||
|
||||
with self.subTest("close"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
raised = BaseException()
|
||||
with self.assertRaises(BaseException) as caught:
|
||||
g.close()
|
||||
self.assertIs(caught.exception, raised)
|
||||
self.assertIsInstance(caught.exception.__context__, GeneratorExit)
|
||||
self.assertIsNone(caught.exception.__context__.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
with self.subTest("throw GeneratorExit"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
raised = BaseException()
|
||||
thrown = GeneratorExit()
|
||||
with self.assertRaises(BaseException) as caught:
|
||||
g.throw(thrown)
|
||||
self.assertIs(caught.exception, raised)
|
||||
# This isn't the same GeneratorExit as thrown! It's the one created
|
||||
# by calling inner.close():
|
||||
self.assertIsInstance(caught.exception.__context__, GeneratorExit)
|
||||
self.assertIsNone(caught.exception.__context__.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
with self.subTest("throw StopIteration"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
raised = BaseException()
|
||||
thrown = StopIteration()
|
||||
with self.assertRaises(BaseException) as caught:
|
||||
g.throw(thrown)
|
||||
self.assertIs(caught.exception, raised)
|
||||
self.assertIs(caught.exception.__context__, thrown)
|
||||
self.assertIsNone(caught.exception.__context__.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
with self.subTest("throw BaseException"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
raised = BaseException()
|
||||
thrown = BaseException()
|
||||
with self.assertRaises(BaseException) as caught:
|
||||
g.throw(thrown)
|
||||
self.assertIs(caught.exception, raised)
|
||||
self.assertIs(caught.exception.__context__, thrown)
|
||||
self.assertIsNone(caught.exception.__context__.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
with self.subTest("throw Exception"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
raised = BaseException()
|
||||
thrown = Exception()
|
||||
with self.assertRaises(BaseException) as caught:
|
||||
g.throw(thrown)
|
||||
self.assertIs(caught.exception, raised)
|
||||
self.assertIs(caught.exception.__context__, thrown)
|
||||
self.assertIsNone(caught.exception.__context__.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
def test_close_and_throw_raise_exception(self):
|
||||
|
||||
yielded_first = object()
|
||||
yielded_second = object()
|
||||
returned = object()
|
||||
|
||||
def inner():
|
||||
try:
|
||||
yield yielded_first
|
||||
yield yielded_second
|
||||
return returned
|
||||
finally:
|
||||
raise raised
|
||||
|
||||
def outer():
|
||||
return (yield from inner())
|
||||
|
||||
with self.subTest("close"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
raised = Exception()
|
||||
with self.assertRaises(Exception) as caught:
|
||||
g.close()
|
||||
self.assertIs(caught.exception, raised)
|
||||
self.assertIsInstance(caught.exception.__context__, GeneratorExit)
|
||||
self.assertIsNone(caught.exception.__context__.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
with self.subTest("throw GeneratorExit"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
raised = Exception()
|
||||
thrown = GeneratorExit()
|
||||
with self.assertRaises(Exception) as caught:
|
||||
g.throw(thrown)
|
||||
self.assertIs(caught.exception, raised)
|
||||
# This isn't the same GeneratorExit as thrown! It's the one created
|
||||
# by calling inner.close():
|
||||
self.assertIsInstance(caught.exception.__context__, GeneratorExit)
|
||||
self.assertIsNone(caught.exception.__context__.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
with self.subTest("throw StopIteration"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
raised = Exception()
|
||||
thrown = StopIteration()
|
||||
with self.assertRaises(Exception) as caught:
|
||||
g.throw(thrown)
|
||||
self.assertIs(caught.exception, raised)
|
||||
self.assertIs(caught.exception.__context__, thrown)
|
||||
self.assertIsNone(caught.exception.__context__.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
with self.subTest("throw BaseException"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
raised = Exception()
|
||||
thrown = BaseException()
|
||||
with self.assertRaises(Exception) as caught:
|
||||
g.throw(thrown)
|
||||
self.assertIs(caught.exception, raised)
|
||||
self.assertIs(caught.exception.__context__, thrown)
|
||||
self.assertIsNone(caught.exception.__context__.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
with self.subTest("throw Exception"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
raised = Exception()
|
||||
thrown = Exception()
|
||||
with self.assertRaises(Exception) as caught:
|
||||
g.throw(thrown)
|
||||
self.assertIs(caught.exception, raised)
|
||||
self.assertIs(caught.exception.__context__, thrown)
|
||||
self.assertIsNone(caught.exception.__context__.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: None is not StopIteration()
|
||||
def test_close_and_throw_yield(self):
|
||||
|
||||
yielded_first = object()
|
||||
yielded_second = object()
|
||||
returned = object()
|
||||
|
||||
def inner():
|
||||
try:
|
||||
yield yielded_first
|
||||
finally:
|
||||
yield yielded_second
|
||||
return returned
|
||||
|
||||
def outer():
|
||||
return (yield from inner())
|
||||
|
||||
with self.subTest("close"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
# No chaining happens. This is consistent with PEP 342:
|
||||
# https://peps.python.org/pep-0342/#new-generator-method-close
|
||||
with self.assert_generator_ignored_generator_exit() as caught:
|
||||
g.close()
|
||||
self.assertIsNone(caught.exception.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
with self.subTest("throw GeneratorExit"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
thrown = GeneratorExit()
|
||||
# No chaining happens. This is consistent with PEP 342:
|
||||
# https://peps.python.org/pep-0342/#new-generator-method-close
|
||||
with self.assert_generator_ignored_generator_exit() as caught:
|
||||
g.throw(thrown)
|
||||
self.assertIsNone(caught.exception.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
with self.subTest("throw StopIteration"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
thrown = StopIteration()
|
||||
self.assertEqual(g.throw(thrown), yielded_second)
|
||||
# PEP 479:
|
||||
with self.assert_generator_raised_stop_iteration() as caught:
|
||||
next(g)
|
||||
self.assertIs(caught.exception.__context__, thrown)
|
||||
self.assertIsNone(caught.exception.__context__.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
with self.subTest("throw BaseException"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
thrown = BaseException()
|
||||
self.assertEqual(g.throw(thrown), yielded_second)
|
||||
with self.assertRaises(BaseException) as caught:
|
||||
next(g)
|
||||
self.assertIs(caught.exception, thrown)
|
||||
self.assertIsNone(caught.exception.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
with self.subTest("throw Exception"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
thrown = Exception()
|
||||
self.assertEqual(g.throw(thrown), yielded_second)
|
||||
with self.assertRaises(Exception) as caught:
|
||||
next(g)
|
||||
self.assertIs(caught.exception, thrown)
|
||||
self.assertIsNone(caught.exception.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_close_and_throw_return(self):
|
||||
|
||||
yielded_first = object()
|
||||
yielded_second = object()
|
||||
returned = object()
|
||||
|
||||
def inner():
|
||||
try:
|
||||
yield yielded_first
|
||||
yield yielded_second
|
||||
finally:
|
||||
return returned
|
||||
|
||||
def outer():
|
||||
return (yield from inner())
|
||||
|
||||
with self.subTest("close"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
# StopIteration is suppressed. This is consistent with PEP 342:
|
||||
# https://peps.python.org/pep-0342/#new-generator-method-close
|
||||
g.close()
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
with self.subTest("throw GeneratorExit"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
thrown = GeneratorExit()
|
||||
# StopIteration is suppressed. This is consistent with PEP 342:
|
||||
# https://peps.python.org/pep-0342/#new-generator-method-close
|
||||
with self.assertRaises(GeneratorExit) as caught:
|
||||
g.throw(thrown)
|
||||
self.assertIs(caught.exception, thrown)
|
||||
self.assertIsNone(caught.exception.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
with self.subTest("throw StopIteration"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
thrown = StopIteration()
|
||||
with self.assertRaises(StopIteration) as caught:
|
||||
g.throw(thrown)
|
||||
self.assertIs(caught.exception.value, returned)
|
||||
self.assertIsNone(caught.exception.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
with self.subTest("throw BaseException"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
thrown = BaseException()
|
||||
with self.assertRaises(StopIteration) as caught:
|
||||
g.throw(thrown)
|
||||
self.assertIs(caught.exception.value, returned)
|
||||
self.assertIsNone(caught.exception.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
with self.subTest("throw Exception"):
|
||||
g = outer()
|
||||
self.assertIs(next(g), yielded_first)
|
||||
thrown = Exception()
|
||||
with self.assertRaises(StopIteration) as caught:
|
||||
g.throw(thrown)
|
||||
self.assertIs(caught.exception.value, returned)
|
||||
self.assertIsNone(caught.exception.__context__)
|
||||
self.assert_stop_iteration(g)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
20
Lib/uuid.py
vendored
20
Lib/uuid.py
vendored
@@ -572,39 +572,43 @@ def _netstat_getnode():
|
||||
try:
|
||||
import _uuid
|
||||
_generate_time_safe = getattr(_uuid, "generate_time_safe", None)
|
||||
_has_stable_extractable_node = getattr(_uuid, "has_stable_extractable_node", False)
|
||||
_UuidCreate = getattr(_uuid, "UuidCreate", None)
|
||||
except ImportError:
|
||||
_uuid = None
|
||||
_generate_time_safe = None
|
||||
_has_stable_extractable_node = False
|
||||
_UuidCreate = None
|
||||
|
||||
|
||||
def _unix_getnode():
|
||||
"""Get the hardware address on Unix using the _uuid extension module."""
|
||||
if _generate_time_safe:
|
||||
if _generate_time_safe and _has_stable_extractable_node:
|
||||
uuid_time, _ = _generate_time_safe()
|
||||
return UUID(bytes=uuid_time).node
|
||||
|
||||
def _windll_getnode():
|
||||
"""Get the hardware address on Windows using the _uuid extension module."""
|
||||
if _UuidCreate:
|
||||
if _UuidCreate and _has_stable_extractable_node:
|
||||
uuid_bytes = _UuidCreate()
|
||||
return UUID(bytes_le=uuid_bytes).node
|
||||
|
||||
def _random_getnode():
|
||||
"""Get a random node ID."""
|
||||
# RFC 4122, $4.1.6 says "For systems with no IEEE address, a randomly or
|
||||
# pseudo-randomly generated value may be used; see Section 4.5. The
|
||||
# multicast bit must be set in such addresses, in order that they will
|
||||
# never conflict with addresses obtained from network cards."
|
||||
# RFC 9562, §6.10-3 says that
|
||||
#
|
||||
# Implementations MAY elect to obtain a 48-bit cryptographic-quality
|
||||
# random number as per Section 6.9 to use as the Node ID. [...] [and]
|
||||
# implementations MUST set the least significant bit of the first octet
|
||||
# of the Node ID to 1. This bit is the unicast or multicast bit, which
|
||||
# will never be set in IEEE 802 addresses obtained from network cards.
|
||||
#
|
||||
# The "multicast bit" of a MAC address is defined to be "the least
|
||||
# significant bit of the first octet". This works out to be the 41st bit
|
||||
# counting from 1 being the least significant bit, or 1<<40.
|
||||
#
|
||||
# See https://en.wikipedia.org/w/index.php?title=MAC_address&oldid=1128764812#Universal_vs._local_(U/L_bit)
|
||||
import random
|
||||
return random.getrandbits(48) | (1 << 40)
|
||||
return int.from_bytes(os.urandom(6)) | (1 << 40)
|
||||
|
||||
|
||||
# _OS_GETTERS, when known, are targeted for a specific OS or platform.
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
use criterion::measurement::WallTime;
|
||||
use criterion::{
|
||||
Bencher, BenchmarkGroup, BenchmarkId, Criterion, Throughput, black_box, criterion_group,
|
||||
criterion_main,
|
||||
Bencher, BenchmarkGroup, BenchmarkId, Criterion, Throughput, criterion_group, criterion_main,
|
||||
measurement::WallTime,
|
||||
};
|
||||
use rustpython_compiler::Mode;
|
||||
use rustpython_vm::{Interpreter, PyResult, Settings};
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::{collections::HashMap, hint::black_box, path::Path};
|
||||
|
||||
fn bench_cpython_code(b: &mut Bencher, source: &str) {
|
||||
let c_str_source_head = std::ffi::CString::new(source).unwrap();
|
||||
let c_str_source = c_str_source_head.as_c_str();
|
||||
pyo3::Python::with_gil(|py| {
|
||||
pyo3::Python::attach(|py| {
|
||||
b.iter(|| {
|
||||
let module = pyo3::types::PyModule::from_code(py, c_str_source, c"", c"")
|
||||
.expect("Error running source");
|
||||
@@ -54,7 +52,7 @@ pub fn benchmark_file_parsing(group: &mut BenchmarkGroup<WallTime>, name: &str,
|
||||
});
|
||||
group.bench_function(BenchmarkId::new("cpython", name), |b| {
|
||||
use pyo3::types::PyAnyMethods;
|
||||
pyo3::Python::with_gil(|py| {
|
||||
pyo3::Python::attach(|py| {
|
||||
let builtins =
|
||||
pyo3::types::PyModule::import(py, "builtins").expect("Failed to import builtins");
|
||||
let compile = builtins.getattr("compile").expect("no compile in builtins");
|
||||
|
||||
@@ -37,7 +37,7 @@ pub struct MicroBenchmark {
|
||||
}
|
||||
|
||||
fn bench_cpython_code(group: &mut BenchmarkGroup<WallTime>, bench: &MicroBenchmark) {
|
||||
pyo3::Python::with_gil(|py| {
|
||||
pyo3::Python::attach(|py| {
|
||||
let setup_name = format!("{}_setup", bench.name);
|
||||
let setup_code = cpy_compile_code(py, &bench.setup, &setup_name).unwrap();
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ license.workspace = true
|
||||
|
||||
[features]
|
||||
threading = ["parking_lot"]
|
||||
wasm_js = ["getrandom/wasm_js"]
|
||||
|
||||
[dependencies]
|
||||
rustpython-literal = { workspace = true }
|
||||
@@ -17,7 +18,6 @@ rustpython-wtf8 = { workspace = true }
|
||||
|
||||
ascii = { workspace = true }
|
||||
bitflags = { workspace = true }
|
||||
bstr = { workspace = true }
|
||||
cfg-if = { workspace = true }
|
||||
getrandom = { workspace = true }
|
||||
itertools = { workspace = true }
|
||||
@@ -25,7 +25,6 @@ libc = { workspace = true }
|
||||
malachite-bigint = { workspace = true }
|
||||
malachite-q = { workspace = true }
|
||||
malachite-base = { workspace = true }
|
||||
memchr = { workspace = true }
|
||||
num-traits = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
parking_lot = { workspace = true, optional = true }
|
||||
|
||||
@@ -18,7 +18,6 @@ ruff_text_size = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rand = { workspace = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -5,7 +5,7 @@ use rustpython_compiler_core::{
|
||||
OneIndexed, SourceLocation,
|
||||
bytecode::{
|
||||
CodeFlags, CodeObject, CodeUnit, ConstantData, InstrDisplayContext, Instruction, Label,
|
||||
OpArg,
|
||||
OpArg, PyCodeLocationInfoKind,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -72,6 +72,7 @@ pub struct InstructionInfo {
|
||||
pub target: BlockIdx,
|
||||
// pub range: TextRange,
|
||||
pub location: SourceLocation,
|
||||
// TODO: end_location for debug ranges
|
||||
}
|
||||
|
||||
// spell-checker:ignore petgraph
|
||||
@@ -199,6 +200,9 @@ impl CodeInfo {
|
||||
locations.clear()
|
||||
}
|
||||
|
||||
// Generate linetable from locations
|
||||
let linetable = generate_linetable(&locations, first_line_number.get() as i32);
|
||||
|
||||
Ok(CodeObject {
|
||||
flags,
|
||||
posonlyarg_count,
|
||||
@@ -218,6 +222,8 @@ impl CodeInfo {
|
||||
cellvars: cellvar_cache.into_iter().collect(),
|
||||
freevars: freevar_cache.into_iter().collect(),
|
||||
cell2arg,
|
||||
linetable,
|
||||
exceptiontable: Box::new([]), // TODO: Generate actual exception table
|
||||
})
|
||||
}
|
||||
|
||||
@@ -388,3 +394,134 @@ fn iter_blocks(blocks: &[Block]) -> impl Iterator<Item = (BlockIdx, &Block)> + '
|
||||
Some((idx, b))
|
||||
})
|
||||
}
|
||||
|
||||
/// Generate CPython 3.11+ format linetable from source locations
|
||||
fn generate_linetable(locations: &[SourceLocation], first_line: i32) -> Box<[u8]> {
|
||||
if locations.is_empty() {
|
||||
return Box::new([]);
|
||||
}
|
||||
|
||||
let mut linetable = Vec::new();
|
||||
// Initialize prev_line to first_line
|
||||
// The first entry's delta is relative to co_firstlineno
|
||||
let mut prev_line = first_line;
|
||||
let mut i = 0;
|
||||
|
||||
while i < locations.len() {
|
||||
let loc = &locations[i];
|
||||
|
||||
// Count consecutive instructions with the same location
|
||||
let mut length = 1;
|
||||
while i + length < locations.len() && locations[i + length] == locations[i] {
|
||||
length += 1;
|
||||
}
|
||||
|
||||
// Process in chunks of up to 8 instructions
|
||||
while length > 0 {
|
||||
let entry_length = length.min(8);
|
||||
|
||||
// Get line and column information
|
||||
// SourceLocation always has row and column (both are OneIndexed)
|
||||
let line = loc.row.get() as i32;
|
||||
let col = (loc.column.get() as i32) - 1; // Convert 1-based to 0-based
|
||||
|
||||
let line_delta = line - prev_line;
|
||||
|
||||
// Choose the appropriate encoding based on line delta and column info
|
||||
// Note: SourceLocation always has valid column, so we never get NO_COLUMNS case
|
||||
if line_delta == 0 {
|
||||
let end_col = col; // Use same column for end (no range info available)
|
||||
|
||||
if col < 80 && end_col - col < 16 && end_col >= col {
|
||||
// Short form (codes 0-9) for common cases
|
||||
let code = (col / 8).min(9) as u8; // Short0 to Short9
|
||||
linetable.push(0x80 | (code << 3) | ((entry_length - 1) as u8));
|
||||
let col_byte = (((col % 8) as u8) << 4) | ((end_col - col) as u8 & 0xf);
|
||||
linetable.push(col_byte);
|
||||
} else if col < 128 && end_col < 128 {
|
||||
// One-line form (code 10) for same line
|
||||
linetable.push(
|
||||
0x80 | ((PyCodeLocationInfoKind::OneLine0 as u8) << 3)
|
||||
| ((entry_length - 1) as u8),
|
||||
);
|
||||
linetable.push(col as u8);
|
||||
linetable.push(end_col as u8);
|
||||
} else {
|
||||
// Long form for columns >= 128
|
||||
linetable.push(
|
||||
0x80 | ((PyCodeLocationInfoKind::Long as u8) << 3)
|
||||
| ((entry_length - 1) as u8),
|
||||
);
|
||||
write_signed_varint(&mut linetable, 0); // line_delta = 0
|
||||
write_varint(&mut linetable, 0); // end_line delta = 0
|
||||
write_varint(&mut linetable, (col as u32) + 1); // column + 1 for encoding
|
||||
write_varint(&mut linetable, (end_col as u32) + 1); // end_col + 1
|
||||
}
|
||||
} else if line_delta > 0 && line_delta < 3
|
||||
/* && column.is_some() */
|
||||
{
|
||||
// One-line form (codes 11-12) for line deltas 1-2
|
||||
let end_col = col; // Use same column for end
|
||||
|
||||
if col < 128 && end_col < 128 {
|
||||
let code = (PyCodeLocationInfoKind::OneLine0 as u8) + (line_delta as u8); // 11 for delta=1, 12 for delta=2
|
||||
linetable.push(0x80 | (code << 3) | ((entry_length - 1) as u8));
|
||||
linetable.push(col as u8);
|
||||
linetable.push(end_col as u8);
|
||||
} else {
|
||||
// Long form for columns >= 128 or negative line delta
|
||||
linetable.push(
|
||||
0x80 | ((PyCodeLocationInfoKind::Long as u8) << 3)
|
||||
| ((entry_length - 1) as u8),
|
||||
);
|
||||
write_signed_varint(&mut linetable, line_delta);
|
||||
write_varint(&mut linetable, 0); // end_line delta = 0
|
||||
write_varint(&mut linetable, (col as u32) + 1); // column + 1 for encoding
|
||||
write_varint(&mut linetable, (end_col as u32) + 1); // end_col + 1
|
||||
}
|
||||
} else {
|
||||
// Long form (code 14) for all other cases
|
||||
// This handles: line_delta < 0, line_delta >= 3, or columns >= 128
|
||||
let end_col = col; // Use same column for end
|
||||
linetable.push(
|
||||
0x80 | ((PyCodeLocationInfoKind::Long as u8) << 3) | ((entry_length - 1) as u8),
|
||||
);
|
||||
write_signed_varint(&mut linetable, line_delta);
|
||||
write_varint(&mut linetable, 0); // end_line delta = 0
|
||||
write_varint(&mut linetable, (col as u32) + 1); // column + 1 for encoding
|
||||
write_varint(&mut linetable, (end_col as u32) + 1); // end_col + 1
|
||||
}
|
||||
|
||||
prev_line = line;
|
||||
length -= entry_length;
|
||||
i += entry_length;
|
||||
}
|
||||
}
|
||||
|
||||
linetable.into_boxed_slice()
|
||||
}
|
||||
|
||||
/// Write a variable-length unsigned integer (6-bit chunks)
|
||||
/// Returns the number of bytes written
|
||||
fn write_varint(buf: &mut Vec<u8>, mut val: u32) -> usize {
|
||||
let start_len = buf.len();
|
||||
while val >= 64 {
|
||||
buf.push(0x40 | (val & 0x3f) as u8);
|
||||
val >>= 6;
|
||||
}
|
||||
buf.push(val as u8);
|
||||
buf.len() - start_len
|
||||
}
|
||||
|
||||
/// Write a variable-length signed integer
|
||||
/// Returns the number of bytes written
|
||||
fn write_signed_varint(buf: &mut Vec<u8>, val: i32) -> usize {
|
||||
let uval = if val < 0 {
|
||||
// (unsigned int)(-val) has an undefined behavior for INT_MIN
|
||||
// So we use (0 - val as u32) to handle it correctly
|
||||
((0u32.wrapping_sub(val as u32)) << 1) | 1
|
||||
} else {
|
||||
(val as u32) << 1
|
||||
};
|
||||
write_varint(buf, uval)
|
||||
}
|
||||
|
||||
@@ -17,9 +17,8 @@ bitflags = { workspace = true }
|
||||
itertools = { workspace = true }
|
||||
malachite-bigint = { workspace = true }
|
||||
num-complex = { workspace = true }
|
||||
serde = { workspace = true, optional = true, default-features = false, features = ["derive"] }
|
||||
|
||||
lz4_flex = "0.11"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
workspace = true
|
||||
|
||||
@@ -33,6 +33,75 @@ pub enum ResumeType {
|
||||
AfterAwait = 3,
|
||||
}
|
||||
|
||||
/// CPython 3.11+ linetable location info codes
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
pub enum PyCodeLocationInfoKind {
|
||||
// Short forms are 0 to 9
|
||||
Short0 = 0,
|
||||
Short1 = 1,
|
||||
Short2 = 2,
|
||||
Short3 = 3,
|
||||
Short4 = 4,
|
||||
Short5 = 5,
|
||||
Short6 = 6,
|
||||
Short7 = 7,
|
||||
Short8 = 8,
|
||||
Short9 = 9,
|
||||
// One line forms are 10 to 12
|
||||
OneLine0 = 10,
|
||||
OneLine1 = 11,
|
||||
OneLine2 = 12,
|
||||
NoColumns = 13,
|
||||
Long = 14,
|
||||
None = 15,
|
||||
}
|
||||
|
||||
impl PyCodeLocationInfoKind {
|
||||
pub fn from_code(code: u8) -> Option<Self> {
|
||||
match code {
|
||||
0 => Some(Self::Short0),
|
||||
1 => Some(Self::Short1),
|
||||
2 => Some(Self::Short2),
|
||||
3 => Some(Self::Short3),
|
||||
4 => Some(Self::Short4),
|
||||
5 => Some(Self::Short5),
|
||||
6 => Some(Self::Short6),
|
||||
7 => Some(Self::Short7),
|
||||
8 => Some(Self::Short8),
|
||||
9 => Some(Self::Short9),
|
||||
10 => Some(Self::OneLine0),
|
||||
11 => Some(Self::OneLine1),
|
||||
12 => Some(Self::OneLine2),
|
||||
13 => Some(Self::NoColumns),
|
||||
14 => Some(Self::Long),
|
||||
15 => Some(Self::None),
|
||||
_ => Option::None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_short(&self) -> bool {
|
||||
(*self as u8) <= 9
|
||||
}
|
||||
|
||||
pub fn short_column_group(&self) -> Option<u8> {
|
||||
if self.is_short() {
|
||||
Some(*self as u8)
|
||||
} else {
|
||||
Option::None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn one_line_delta(&self) -> Option<i32> {
|
||||
match self {
|
||||
Self::OneLine0 => Some(0),
|
||||
Self::OneLine1 => Some(1),
|
||||
Self::OneLine2 => Some(2),
|
||||
_ => Option::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Constant: Sized {
|
||||
type Name: AsRef<str>;
|
||||
|
||||
@@ -129,23 +198,27 @@ pub struct CodeObject<C: Constant = ConstantData> {
|
||||
pub instructions: Box<[CodeUnit]>,
|
||||
pub locations: Box<[SourceLocation]>,
|
||||
pub flags: CodeFlags,
|
||||
/// Number of positional-only arguments
|
||||
pub posonlyarg_count: u32,
|
||||
// Number of positional-only arguments
|
||||
pub arg_count: u32,
|
||||
pub kwonlyarg_count: u32,
|
||||
pub source_path: C::Name,
|
||||
pub first_line_number: Option<OneIndexed>,
|
||||
pub max_stackdepth: u32,
|
||||
/// Name of the object that created this code object
|
||||
pub obj_name: C::Name,
|
||||
// Name of the object that created this code object
|
||||
/// Qualified name of the object (like CPython's co_qualname)
|
||||
pub qualname: C::Name,
|
||||
// Qualified name of the object (like CPython's co_qualname)
|
||||
pub cell2arg: Option<Box<[i32]>>,
|
||||
pub constants: Box<[C]>,
|
||||
pub names: Box<[C::Name]>,
|
||||
pub varnames: Box<[C::Name]>,
|
||||
pub cellvars: Box<[C::Name]>,
|
||||
pub freevars: Box<[C::Name]>,
|
||||
/// Line number table (CPython 3.11+ format)
|
||||
pub linetable: Box<[u8]>,
|
||||
/// Exception handling table
|
||||
pub exceptiontable: Box<[u8]>,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
@@ -221,6 +294,12 @@ impl OpArg {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for OpArg {
|
||||
fn from(raw: u32) -> Self {
|
||||
Self(raw)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Copy, Clone)]
|
||||
#[repr(transparent)]
|
||||
pub struct OpArgState {
|
||||
@@ -1202,6 +1281,8 @@ impl<C: Constant> CodeObject<C> {
|
||||
first_line_number: self.first_line_number,
|
||||
max_stackdepth: self.max_stackdepth,
|
||||
cell2arg: self.cell2arg,
|
||||
linetable: self.linetable,
|
||||
exceptiontable: self.exceptiontable,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1232,6 +1313,8 @@ impl<C: Constant> CodeObject<C> {
|
||||
first_line_number: self.first_line_number,
|
||||
max_stackdepth: self.max_stackdepth,
|
||||
cell2arg: self.cell2arg.clone(),
|
||||
linetable: self.linetable.clone(),
|
||||
exceptiontable: self.exceptiontable.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,6 +251,16 @@ pub fn deserialize_code<R: Read, Bag: ConstantBag>(
|
||||
let cellvars = read_names()?;
|
||||
let freevars = read_names()?;
|
||||
|
||||
// Read linetable and exceptiontable
|
||||
let linetable_len = rdr.read_u32()?;
|
||||
let linetable = rdr.read_slice(linetable_len)?.to_vec().into_boxed_slice();
|
||||
|
||||
let exceptiontable_len = rdr.read_u32()?;
|
||||
let exceptiontable = rdr
|
||||
.read_slice(exceptiontable_len)?
|
||||
.to_vec()
|
||||
.into_boxed_slice();
|
||||
|
||||
Ok(CodeObject {
|
||||
instructions,
|
||||
locations,
|
||||
@@ -269,6 +279,8 @@ pub fn deserialize_code<R: Read, Bag: ConstantBag>(
|
||||
varnames,
|
||||
cellvars,
|
||||
freevars,
|
||||
linetable,
|
||||
exceptiontable,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -684,4 +696,8 @@ pub fn serialize_code<W: Write, C: Constant>(buf: &mut W, code: &CodeObject<C>)
|
||||
write_names(&code.varnames);
|
||||
write_names(&code.cellvars);
|
||||
write_names(&code.freevars);
|
||||
|
||||
// Serialize linetable and exceptiontable
|
||||
write_vec(buf, &code.linetable);
|
||||
write_vec(buf, &code.exceptiontable);
|
||||
}
|
||||
|
||||
@@ -14,8 +14,7 @@ proc-macro = true
|
||||
[dependencies]
|
||||
rustpython-compiler = { workspace = true }
|
||||
rustpython-derive-impl = { workspace = true }
|
||||
proc-macro2 = { workspace = true }
|
||||
syn = { workspace = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
workspace = true
|
||||
|
||||
@@ -22,7 +22,7 @@ tkinter = ["dep:tk-sys", "dep:tcl-sys"]
|
||||
[dependencies]
|
||||
# rustpython crates
|
||||
rustpython-derive = { workspace = true }
|
||||
rustpython-vm = { workspace = true, default-features = false }
|
||||
rustpython-vm = { workspace = true, default-features = false, features = ["compiler"]}
|
||||
rustpython-common = { workspace = true }
|
||||
|
||||
ahash = { workspace = true }
|
||||
@@ -117,7 +117,6 @@ lzma-sys = "0.1"
|
||||
xz2 = "0.1"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
junction = { workspace = true }
|
||||
paste = { workspace = true }
|
||||
schannel = { workspace = true }
|
||||
widestring = { workspace = true }
|
||||
|
||||
@@ -38,6 +38,7 @@ mod locale;
|
||||
mod math;
|
||||
#[cfg(unix)]
|
||||
mod mmap;
|
||||
mod opcode;
|
||||
mod pyexpat;
|
||||
mod pystruct;
|
||||
mod random;
|
||||
@@ -135,6 +136,7 @@ pub fn get_module_inits() -> impl Iterator<Item = (Cow<'static, str>, StdlibInit
|
||||
"_json" => json::make_module,
|
||||
"math" => math::make_module,
|
||||
"pyexpat" => pyexpat::make_module,
|
||||
"_opcode" => opcode::make_module,
|
||||
"_random" => random::make_module,
|
||||
"_statistics" => statistics::make_module,
|
||||
"_struct" => pystruct::make_module,
|
||||
|
||||
282
stdlib/src/opcode.rs
Normal file
282
stdlib/src/opcode.rs
Normal file
@@ -0,0 +1,282 @@
|
||||
pub(crate) use opcode::make_module;
|
||||
|
||||
#[pymodule]
|
||||
mod opcode {
|
||||
use crate::vm::{
|
||||
AsObject, PyObjectRef, PyResult, VirtualMachine,
|
||||
builtins::{PyBool, PyInt, PyIntRef, PyNone},
|
||||
bytecode::Instruction,
|
||||
match_class,
|
||||
};
|
||||
use std::ops::Deref;
|
||||
|
||||
struct Opcode(Instruction);
|
||||
|
||||
impl Deref for Opcode {
|
||||
type Target = Instruction;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Opcode {
|
||||
// https://github.com/python/cpython/blob/bcee1c322115c581da27600f2ae55e5439c027eb/Include/opcode_ids.h#L238
|
||||
const HAVE_ARGUMENT: i32 = 44;
|
||||
|
||||
pub fn try_from_pyint(raw: PyIntRef, vm: &VirtualMachine) -> PyResult<Self> {
|
||||
let instruction = raw
|
||||
.try_to_primitive::<u8>(vm)
|
||||
.and_then(|v| {
|
||||
Instruction::try_from(v).map_err(|_| {
|
||||
vm.new_exception_empty(vm.ctx.exceptions.value_error.to_owned())
|
||||
})
|
||||
})
|
||||
.map_err(|_| vm.new_value_error("invalid opcode or oparg"))?;
|
||||
|
||||
Ok(Self(instruction))
|
||||
}
|
||||
|
||||
/// https://github.com/python/cpython/blob/bcee1c322115c581da27600f2ae55e5439c027eb/Include/internal/pycore_opcode_metadata.h#L914-L916
|
||||
#[must_use]
|
||||
pub const fn is_valid(opcode: i32) -> bool {
|
||||
opcode >= 0 && opcode < 268 && opcode != 255
|
||||
}
|
||||
|
||||
// All `has_*` methods below mimics
|
||||
// https://github.com/python/cpython/blob/bcee1c322115c581da27600f2ae55e5439c027eb/Include/internal/pycore_opcode_metadata.h#L966-L1190
|
||||
|
||||
#[must_use]
|
||||
pub const fn has_arg(opcode: i32) -> bool {
|
||||
Self::is_valid(opcode) && opcode > Self::HAVE_ARGUMENT
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn has_const(opcode: i32) -> bool {
|
||||
Self::is_valid(opcode) && matches!(opcode, 83 | 103 | 240)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn has_name(opcode: i32) -> bool {
|
||||
Self::is_valid(opcode)
|
||||
&& matches!(
|
||||
opcode,
|
||||
63 | 66
|
||||
| 67
|
||||
| 74
|
||||
| 75
|
||||
| 82
|
||||
| 90
|
||||
| 91
|
||||
| 92
|
||||
| 93
|
||||
| 108
|
||||
| 113
|
||||
| 114
|
||||
| 259
|
||||
| 260
|
||||
| 261
|
||||
| 262
|
||||
)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn has_jump(opcode: i32) -> bool {
|
||||
Self::is_valid(opcode)
|
||||
&& matches!(
|
||||
opcode,
|
||||
72 | 77 | 78 | 79 | 97 | 98 | 99 | 100 | 104 | 256 | 257
|
||||
)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn has_free(opcode: i32) -> bool {
|
||||
Self::is_valid(opcode) && matches!(opcode, 64 | 84 | 89 | 94 | 109)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn has_local(opcode: i32) -> bool {
|
||||
Self::is_valid(opcode)
|
||||
&& matches!(opcode, 65 | 85 | 86 | 87 | 88 | 110 | 111 | 112 | 258 | 267)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn has_exc(opcode: i32) -> bool {
|
||||
Self::is_valid(opcode) && matches!(opcode, 264..=266)
|
||||
}
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
const ENABLE_SPECIALIZATION: i8 = 1;
|
||||
|
||||
#[derive(FromArgs)]
|
||||
struct StackEffectArgs {
|
||||
#[pyarg(positional)]
|
||||
opcode: PyIntRef,
|
||||
#[pyarg(positional, optional)]
|
||||
oparg: Option<PyObjectRef>,
|
||||
#[pyarg(named, optional)]
|
||||
jump: Option<PyObjectRef>,
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn stack_effect(args: StackEffectArgs, vm: &VirtualMachine) -> PyResult<i32> {
|
||||
let oparg = args
|
||||
.oparg
|
||||
.map(|v| {
|
||||
if !v.fast_isinstance(vm.ctx.types.int_type) {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"'{}' object cannot be interpreted as an integer",
|
||||
v.class().name()
|
||||
)));
|
||||
}
|
||||
v.downcast_ref::<PyInt>()
|
||||
.ok_or_else(|| vm.new_type_error(""))?
|
||||
.try_to_primitive::<u32>(vm)
|
||||
})
|
||||
.unwrap_or(Ok(0))?;
|
||||
|
||||
let jump = args
|
||||
.jump
|
||||
.map(|v| {
|
||||
match_class!(match v {
|
||||
b @ PyBool => Ok(b.is(&vm.ctx.true_value)),
|
||||
_n @ PyNone => Ok(false),
|
||||
_ => {
|
||||
Err(vm.new_value_error("stack_effect: jump must be False, True or None"))
|
||||
}
|
||||
})
|
||||
})
|
||||
.unwrap_or(Ok(false))?;
|
||||
|
||||
let opcode = Opcode::try_from_pyint(args.opcode, vm)?;
|
||||
|
||||
Ok(opcode.stack_effect(oparg.into(), jump))
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn is_valid(opcode: i32) -> bool {
|
||||
Opcode::is_valid(opcode)
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn has_arg(opcode: i32) -> bool {
|
||||
Opcode::has_arg(opcode)
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn has_const(opcode: i32) -> bool {
|
||||
Opcode::has_const(opcode)
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn has_name(opcode: i32) -> bool {
|
||||
Opcode::has_name(opcode)
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn has_jump(opcode: i32) -> bool {
|
||||
Opcode::has_jump(opcode)
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn has_free(opcode: i32) -> bool {
|
||||
Opcode::has_free(opcode)
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn has_local(opcode: i32) -> bool {
|
||||
Opcode::has_local(opcode)
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn has_exc(opcode: i32) -> bool {
|
||||
Opcode::has_exc(opcode)
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn get_intrinsic1_descs(vm: &VirtualMachine) -> Vec<PyObjectRef> {
|
||||
[
|
||||
"INTRINSIC_1_INVALID",
|
||||
"INTRINSIC_PRINT",
|
||||
"INTRINSIC_IMPORT_STAR",
|
||||
"INTRINSIC_STOPITERATION_ERROR",
|
||||
"INTRINSIC_ASYNC_GEN_WRAP",
|
||||
"INTRINSIC_UNARY_POSITIVE",
|
||||
"INTRINSIC_LIST_TO_TUPLE",
|
||||
"INTRINSIC_TYPEVAR",
|
||||
"INTRINSIC_PARAMSPEC",
|
||||
"INTRINSIC_TYPEVARTUPLE",
|
||||
"INTRINSIC_SUBSCRIPT_GENERIC",
|
||||
"INTRINSIC_TYPEALIAS",
|
||||
]
|
||||
.into_iter()
|
||||
.map(|x| vm.ctx.new_str(x).into())
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn get_intrinsic2_descs(vm: &VirtualMachine) -> Vec<PyObjectRef> {
|
||||
[
|
||||
"INTRINSIC_2_INVALID",
|
||||
"INTRINSIC_PREP_RERAISE_STAR",
|
||||
"INTRINSIC_TYPEVAR_WITH_BOUND",
|
||||
"INTRINSIC_TYPEVAR_WITH_CONSTRAINTS",
|
||||
"INTRINSIC_SET_FUNCTION_TYPE_PARAMS",
|
||||
"INTRINSIC_SET_TYPEPARAM_DEFAULT",
|
||||
]
|
||||
.into_iter()
|
||||
.map(|x| vm.ctx.new_str(x).into())
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn get_nb_ops(vm: &VirtualMachine) -> Vec<PyObjectRef> {
|
||||
[
|
||||
("NB_ADD", "+"),
|
||||
("NB_AND", "&"),
|
||||
("NB_FLOOR_DIVIDE", "//"),
|
||||
("NB_LSHIFT", "<<"),
|
||||
("NB_MATRIX_MULTIPLY", "@"),
|
||||
("NB_MULTIPLY", "*"),
|
||||
("NB_REMAINDER", "%"),
|
||||
("NB_OR", "|"),
|
||||
("NB_POWER", "**"),
|
||||
("NB_RSHIFT", ">>"),
|
||||
("NB_SUBTRACT", "-"),
|
||||
("NB_TRUE_DIVIDE", "/"),
|
||||
("NB_XOR", "^"),
|
||||
("NB_INPLACE_ADD", "+="),
|
||||
("NB_INPLACE_AND", "&="),
|
||||
("NB_INPLACE_FLOOR_DIVIDE", "//="),
|
||||
("NB_INPLACE_LSHIFT", "<<="),
|
||||
("NB_INPLACE_MATRIX_MULTIPLY", "@="),
|
||||
("NB_INPLACE_MULTIPLY", "*="),
|
||||
("NB_INPLACE_REMAINDER", "%="),
|
||||
("NB_INPLACE_OR", "|="),
|
||||
("NB_INPLACE_POWER", "**="),
|
||||
("NB_INPLACE_RSHIFT", ">>="),
|
||||
("NB_INPLACE_SUBTRACT", "-="),
|
||||
("NB_INPLACE_TRUE_DIVIDE", "/="),
|
||||
("NB_INPLACE_XOR", "^="),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|(a, b)| {
|
||||
vm.ctx
|
||||
.new_tuple(vec![vm.ctx.new_str(a).into(), vm.ctx.new_str(b).into()])
|
||||
.into()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn get_executor(_code: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
|
||||
// TODO
|
||||
Ok(vm.ctx.none())
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn get_specialization_stats(vm: &VirtualMachine) -> PyObjectRef {
|
||||
vm.ctx.none()
|
||||
}
|
||||
}
|
||||
@@ -30,4 +30,7 @@ mod _uuid {
|
||||
fn has_uuid_generate_time_safe(_vm: &VirtualMachine) -> u32 {
|
||||
0
|
||||
}
|
||||
|
||||
#[pyattr(name = "has_stable_extractable_node")]
|
||||
const HAS_STABLE_EXTRACTABLE_NODE: bool = false;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ ast = ["ruff_python_ast", "ruff_text_size"]
|
||||
codegen = ["rustpython-codegen", "ast"]
|
||||
parser = ["ast"]
|
||||
serde = ["dep:serde"]
|
||||
wasmbind = ["chrono/wasmbind", "getrandom/wasm_js", "wasm-bindgen"]
|
||||
wasmbind = ["rustpython-common/wasm_js", "chrono/wasmbind", "wasm-bindgen"]
|
||||
|
||||
[dependencies]
|
||||
rustpython-compiler = { workspace = true, optional = true }
|
||||
@@ -56,7 +56,6 @@ itertools = { workspace = true }
|
||||
is-macro = { workspace = true }
|
||||
libc = { workspace = true }
|
||||
log = { workspace = true }
|
||||
nix = { workspace = true }
|
||||
malachite-bigint = { workspace = true }
|
||||
num-complex = { workspace = true }
|
||||
num-integer = { workspace = true }
|
||||
@@ -76,15 +75,13 @@ thread_local = { workspace = true }
|
||||
memchr = { workspace = true }
|
||||
|
||||
caseless = "0.2.2"
|
||||
flamer = { version = "0.4", optional = true }
|
||||
flamer = { version = "0.5", optional = true }
|
||||
half = "2"
|
||||
memoffset = "0.9.1"
|
||||
optional = { workspace = true }
|
||||
result-like = "0.5.0"
|
||||
timsort = "0.1.2"
|
||||
|
||||
## unicode stuff
|
||||
unicode_names2 = { workspace = true }
|
||||
# TODO: use unic for this; needed for title case:
|
||||
# https://github.com/RustPython/RustPython/pull/832#discussion_r275428939
|
||||
unicode-casing = { workspace = true }
|
||||
@@ -95,6 +92,7 @@ unic-ucd-ident = { workspace = true }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
rustix = { workspace = true }
|
||||
nix = { workspace = true }
|
||||
exitcode = "1.1.2"
|
||||
uname = "0.1.1"
|
||||
|
||||
@@ -113,7 +111,6 @@ num_cpus = "1.17.0"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
junction = { workspace = true }
|
||||
schannel = { workspace = true }
|
||||
winreg = "0.55"
|
||||
|
||||
[target.'cfg(windows)'.dependencies.windows]
|
||||
|
||||
@@ -16,8 +16,125 @@ use crate::{
|
||||
use malachite_bigint::BigInt;
|
||||
use num_traits::Zero;
|
||||
use rustpython_compiler_core::OneIndexed;
|
||||
use rustpython_compiler_core::bytecode::PyCodeLocationInfoKind;
|
||||
use std::{borrow::Borrow, fmt, ops::Deref};
|
||||
|
||||
/// State for iterating through code address ranges
|
||||
struct PyCodeAddressRange<'a> {
|
||||
ar_start: i32,
|
||||
ar_end: i32,
|
||||
ar_line: i32,
|
||||
computed_line: i32,
|
||||
reader: LineTableReader<'a>,
|
||||
}
|
||||
|
||||
impl<'a> PyCodeAddressRange<'a> {
|
||||
fn new(linetable: &'a [u8], first_line: i32) -> Self {
|
||||
PyCodeAddressRange {
|
||||
ar_start: 0,
|
||||
ar_end: 0,
|
||||
ar_line: -1,
|
||||
computed_line: first_line,
|
||||
reader: LineTableReader::new(linetable),
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if this is a NO_LINE marker (code 15)
|
||||
fn is_no_line_marker(byte: u8) -> bool {
|
||||
(byte >> 3) == 0x1f
|
||||
}
|
||||
|
||||
/// Advance to next address range
|
||||
fn advance(&mut self) -> bool {
|
||||
if self.reader.at_end() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let first_byte = match self.reader.read_byte() {
|
||||
Some(b) => b,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
if (first_byte & 0x80) == 0 {
|
||||
return false; // Invalid linetable
|
||||
}
|
||||
|
||||
let code = (first_byte >> 3) & 0x0f;
|
||||
let length = ((first_byte & 0x07) + 1) as i32;
|
||||
|
||||
// Get line delta for this entry
|
||||
let line_delta = self.get_line_delta(code);
|
||||
|
||||
// Update computed line
|
||||
self.computed_line += line_delta;
|
||||
|
||||
// Check for NO_LINE marker
|
||||
if Self::is_no_line_marker(first_byte) {
|
||||
self.ar_line = -1;
|
||||
} else {
|
||||
self.ar_line = self.computed_line;
|
||||
}
|
||||
|
||||
// Update address range
|
||||
self.ar_start = self.ar_end;
|
||||
self.ar_end += length * 2; // sizeof(_Py_CODEUNIT) = 2
|
||||
|
||||
// Skip remaining bytes for this entry
|
||||
while !self.reader.at_end() {
|
||||
if let Some(b) = self.reader.peek_byte() {
|
||||
if (b & 0x80) != 0 {
|
||||
break;
|
||||
}
|
||||
self.reader.read_byte();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn get_line_delta(&mut self, code: u8) -> i32 {
|
||||
let kind = match PyCodeLocationInfoKind::from_code(code) {
|
||||
Some(k) => k,
|
||||
None => return 0,
|
||||
};
|
||||
|
||||
match kind {
|
||||
PyCodeLocationInfoKind::None => 0, // NO_LINE marker
|
||||
PyCodeLocationInfoKind::Long => {
|
||||
let delta = self.reader.read_signed_varint();
|
||||
// Skip end_line, col, end_col
|
||||
self.reader.read_varint();
|
||||
self.reader.read_varint();
|
||||
self.reader.read_varint();
|
||||
delta
|
||||
}
|
||||
PyCodeLocationInfoKind::NoColumns => self.reader.read_signed_varint(),
|
||||
PyCodeLocationInfoKind::OneLine0 => {
|
||||
self.reader.read_byte(); // Skip column
|
||||
self.reader.read_byte(); // Skip end column
|
||||
0
|
||||
}
|
||||
PyCodeLocationInfoKind::OneLine1 => {
|
||||
self.reader.read_byte(); // Skip column
|
||||
self.reader.read_byte(); // Skip end column
|
||||
1
|
||||
}
|
||||
PyCodeLocationInfoKind::OneLine2 => {
|
||||
self.reader.read_byte(); // Skip column
|
||||
self.reader.read_byte(); // Skip end column
|
||||
2
|
||||
}
|
||||
_ if kind.is_short() => {
|
||||
self.reader.read_byte(); // Skip column byte
|
||||
0
|
||||
}
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
pub struct ReplaceArgs {
|
||||
#[pyarg(named, optional)]
|
||||
@@ -40,6 +157,22 @@ pub struct ReplaceArgs {
|
||||
co_flags: OptionalArg<u16>,
|
||||
#[pyarg(named, optional)]
|
||||
co_varnames: OptionalArg<Vec<PyObjectRef>>,
|
||||
#[pyarg(named, optional)]
|
||||
co_nlocals: OptionalArg<u32>,
|
||||
#[pyarg(named, optional)]
|
||||
co_stacksize: OptionalArg<u32>,
|
||||
#[pyarg(named, optional)]
|
||||
co_code: OptionalArg<crate::builtins::PyBytesRef>,
|
||||
#[pyarg(named, optional)]
|
||||
co_linetable: OptionalArg<crate::builtins::PyBytesRef>,
|
||||
#[pyarg(named, optional)]
|
||||
co_exceptiontable: OptionalArg<crate::builtins::PyBytesRef>,
|
||||
#[pyarg(named, optional)]
|
||||
co_freevars: OptionalArg<Vec<PyObjectRef>>,
|
||||
#[pyarg(named, optional)]
|
||||
co_cellvars: OptionalArg<Vec<PyObjectRef>>,
|
||||
#[pyarg(named, optional)]
|
||||
co_qualname: OptionalArg<PyStrRef>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -350,6 +483,211 @@ impl PyCode {
|
||||
vm.ctx.new_tuple(names)
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
pub fn co_linetable(&self, vm: &VirtualMachine) -> crate::builtins::PyBytesRef {
|
||||
// Return the actual linetable from the code object
|
||||
vm.ctx.new_bytes(self.code.linetable.to_vec())
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
pub fn co_exceptiontable(&self, vm: &VirtualMachine) -> crate::builtins::PyBytesRef {
|
||||
// Return the actual exception table from the code object
|
||||
vm.ctx.new_bytes(self.code.exceptiontable.to_vec())
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
pub fn co_lines(&self, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
|
||||
// TODO: Implement lazy iterator (lineiterator) like CPython for better performance
|
||||
// Currently returns eager list for simplicity
|
||||
|
||||
// Return an iterator over (start_offset, end_offset, lineno) tuples
|
||||
let linetable = self.code.linetable.as_ref();
|
||||
let mut lines = Vec::new();
|
||||
|
||||
if !linetable.is_empty() {
|
||||
let first_line = self.code.first_line_number.map_or(0, |n| n.get() as i32);
|
||||
let mut range = PyCodeAddressRange::new(linetable, first_line);
|
||||
|
||||
// Process all address ranges and merge consecutive entries with same line
|
||||
let mut pending_entry: Option<(i32, i32, i32)> = None;
|
||||
|
||||
while range.advance() {
|
||||
let start = range.ar_start;
|
||||
let end = range.ar_end;
|
||||
let line = range.ar_line;
|
||||
|
||||
if let Some((prev_start, _, prev_line)) = pending_entry {
|
||||
if prev_line == line {
|
||||
// Same line, extend the range
|
||||
pending_entry = Some((prev_start, end, prev_line));
|
||||
} else {
|
||||
// Different line, emit the previous entry
|
||||
let tuple = if prev_line == -1 {
|
||||
vm.ctx.new_tuple(vec![
|
||||
vm.ctx.new_int(prev_start).into(),
|
||||
vm.ctx.new_int(start).into(),
|
||||
vm.ctx.none(),
|
||||
])
|
||||
} else {
|
||||
vm.ctx.new_tuple(vec![
|
||||
vm.ctx.new_int(prev_start).into(),
|
||||
vm.ctx.new_int(start).into(),
|
||||
vm.ctx.new_int(prev_line).into(),
|
||||
])
|
||||
};
|
||||
lines.push(tuple.into());
|
||||
pending_entry = Some((start, end, line));
|
||||
}
|
||||
} else {
|
||||
// First entry
|
||||
pending_entry = Some((start, end, line));
|
||||
}
|
||||
}
|
||||
|
||||
// Emit the last pending entry
|
||||
if let Some((start, end, line)) = pending_entry {
|
||||
let tuple = if line == -1 {
|
||||
vm.ctx.new_tuple(vec![
|
||||
vm.ctx.new_int(start).into(),
|
||||
vm.ctx.new_int(end).into(),
|
||||
vm.ctx.none(),
|
||||
])
|
||||
} else {
|
||||
vm.ctx.new_tuple(vec![
|
||||
vm.ctx.new_int(start).into(),
|
||||
vm.ctx.new_int(end).into(),
|
||||
vm.ctx.new_int(line).into(),
|
||||
])
|
||||
};
|
||||
lines.push(tuple.into());
|
||||
}
|
||||
}
|
||||
|
||||
let list = vm.ctx.new_list(lines);
|
||||
vm.call_method(list.as_object(), "__iter__", ())
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
pub fn co_positions(&self, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
|
||||
// Return an iterator over (line, end_line, column, end_column) tuples for each instruction
|
||||
let linetable = self.code.linetable.as_ref();
|
||||
let mut positions = Vec::new();
|
||||
|
||||
if !linetable.is_empty() {
|
||||
let mut reader = LineTableReader::new(linetable);
|
||||
let mut line = self.code.first_line_number.map_or(0, |n| n.get() as i32);
|
||||
|
||||
while !reader.at_end() {
|
||||
let first_byte = match reader.read_byte() {
|
||||
Some(b) => b,
|
||||
None => break,
|
||||
};
|
||||
|
||||
if (first_byte & 0x80) == 0 {
|
||||
break; // Invalid linetable
|
||||
}
|
||||
|
||||
let code = (first_byte >> 3) & 0x0f;
|
||||
let length = ((first_byte & 0x07) + 1) as i32;
|
||||
|
||||
let kind = match PyCodeLocationInfoKind::from_code(code) {
|
||||
Some(k) => k,
|
||||
None => break, // Invalid code
|
||||
};
|
||||
|
||||
let (line_delta, end_line_delta, column, end_column): (
|
||||
i32,
|
||||
i32,
|
||||
Option<i32>,
|
||||
Option<i32>,
|
||||
) = match kind {
|
||||
PyCodeLocationInfoKind::None => {
|
||||
// No location - all values are None
|
||||
(0, 0, None, None)
|
||||
}
|
||||
PyCodeLocationInfoKind::Long => {
|
||||
// Long form
|
||||
let delta = reader.read_signed_varint();
|
||||
let end_line_delta = reader.read_varint() as i32;
|
||||
|
||||
let col = reader.read_varint();
|
||||
let column = if col == 0 {
|
||||
None
|
||||
} else {
|
||||
Some((col - 1) as i32)
|
||||
};
|
||||
|
||||
let end_col = reader.read_varint();
|
||||
let end_column = if end_col == 0 {
|
||||
None
|
||||
} else {
|
||||
Some((end_col - 1) as i32)
|
||||
};
|
||||
|
||||
// endline = line + end_line_delta (will be computed after line update)
|
||||
(delta, end_line_delta, column, end_column)
|
||||
}
|
||||
PyCodeLocationInfoKind::NoColumns => {
|
||||
// No column form
|
||||
let delta = reader.read_signed_varint();
|
||||
(delta, 0, None, None) // endline will be same as line (delta = 0)
|
||||
}
|
||||
PyCodeLocationInfoKind::OneLine0
|
||||
| PyCodeLocationInfoKind::OneLine1
|
||||
| PyCodeLocationInfoKind::OneLine2 => {
|
||||
// One-line form - endline = line
|
||||
let col = reader.read_byte().unwrap_or(0) as i32;
|
||||
let end_col = reader.read_byte().unwrap_or(0) as i32;
|
||||
let delta = kind.one_line_delta().unwrap_or(0);
|
||||
(delta, 0, Some(col), Some(end_col)) // endline = line (delta = 0)
|
||||
}
|
||||
_ if kind.is_short() => {
|
||||
// Short form - endline = line
|
||||
let col_data = reader.read_byte().unwrap_or(0);
|
||||
let col_group = kind.short_column_group().unwrap_or(0);
|
||||
let col = ((col_group as i32) << 3) | ((col_data >> 4) as i32);
|
||||
let end_col = col + (col_data & 0x0f) as i32;
|
||||
(0, 0, Some(col), Some(end_col)) // endline = line (delta = 0)
|
||||
}
|
||||
_ => (0, 0, None, None),
|
||||
};
|
||||
|
||||
// Update line number
|
||||
line += line_delta;
|
||||
|
||||
// Generate position tuples for each instruction covered by this entry
|
||||
for _ in 0..length {
|
||||
// Handle special case for no location (code 15)
|
||||
let final_line = if kind == PyCodeLocationInfoKind::None {
|
||||
None
|
||||
} else {
|
||||
Some(line)
|
||||
};
|
||||
|
||||
let final_endline = if kind == PyCodeLocationInfoKind::None {
|
||||
None
|
||||
} else {
|
||||
Some(line + end_line_delta)
|
||||
};
|
||||
|
||||
// Convert Option to PyObject (None or int)
|
||||
let line_obj = final_line.to_pyobject(vm);
|
||||
let end_line_obj = final_endline.to_pyobject(vm);
|
||||
let column_obj = column.to_pyobject(vm);
|
||||
let end_column_obj = end_column.to_pyobject(vm);
|
||||
|
||||
let tuple =
|
||||
vm.ctx
|
||||
.new_tuple(vec![line_obj, end_line_obj, column_obj, end_column_obj]);
|
||||
positions.push(tuple.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let list = vm.ctx.new_list(positions);
|
||||
vm.call_method(list.as_object(), "__iter__", ())
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
pub fn replace(&self, args: ReplaceArgs, vm: &VirtualMachine) -> PyResult<Self> {
|
||||
let posonlyarg_count = match args.co_posonlyargcount {
|
||||
@@ -408,6 +746,66 @@ impl PyCode {
|
||||
OptionalArg::Missing => self.code.varnames.iter().map(|s| s.to_object()).collect(),
|
||||
};
|
||||
|
||||
let qualname = match args.co_qualname {
|
||||
OptionalArg::Present(qualname) => qualname,
|
||||
OptionalArg::Missing => self.code.qualname.to_owned(),
|
||||
};
|
||||
|
||||
let max_stackdepth = match args.co_stacksize {
|
||||
OptionalArg::Present(stacksize) => stacksize,
|
||||
OptionalArg::Missing => self.code.max_stackdepth,
|
||||
};
|
||||
|
||||
let instructions = match args.co_code {
|
||||
OptionalArg::Present(_code_bytes) => {
|
||||
// Convert bytes back to instructions
|
||||
// For now, keep the original instructions
|
||||
// TODO: Properly parse bytecode from bytes
|
||||
self.code.instructions.clone()
|
||||
}
|
||||
OptionalArg::Missing => self.code.instructions.clone(),
|
||||
};
|
||||
|
||||
let cellvars = match args.co_cellvars {
|
||||
OptionalArg::Present(cellvars) => cellvars
|
||||
.into_iter()
|
||||
.map(|o| o.as_interned_str(vm).unwrap())
|
||||
.collect(),
|
||||
OptionalArg::Missing => self.code.cellvars.clone(),
|
||||
};
|
||||
|
||||
let freevars = match args.co_freevars {
|
||||
OptionalArg::Present(freevars) => freevars
|
||||
.into_iter()
|
||||
.map(|o| o.as_interned_str(vm).unwrap())
|
||||
.collect(),
|
||||
OptionalArg::Missing => self.code.freevars.clone(),
|
||||
};
|
||||
|
||||
// Validate co_nlocals if provided
|
||||
if let OptionalArg::Present(nlocals) = args.co_nlocals
|
||||
&& nlocals as usize != varnames.len()
|
||||
{
|
||||
return Err(vm.new_value_error(format!(
|
||||
"co_nlocals ({}) != len(co_varnames) ({})",
|
||||
nlocals,
|
||||
varnames.len()
|
||||
)));
|
||||
}
|
||||
|
||||
// Handle linetable and exceptiontable
|
||||
let linetable = match args.co_linetable {
|
||||
OptionalArg::Present(linetable) => linetable.as_bytes().to_vec().into_boxed_slice(),
|
||||
OptionalArg::Missing => self.code.linetable.clone(),
|
||||
};
|
||||
|
||||
let exceptiontable = match args.co_exceptiontable {
|
||||
OptionalArg::Present(exceptiontable) => {
|
||||
exceptiontable.as_bytes().to_vec().into_boxed_slice()
|
||||
}
|
||||
OptionalArg::Missing => self.code.exceptiontable.clone(),
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
code: CodeObject {
|
||||
flags: CodeFlags::from_bits_truncate(flags),
|
||||
@@ -417,10 +815,10 @@ impl PyCode {
|
||||
source_path: source_path.as_object().as_interned_str(vm).unwrap(),
|
||||
first_line_number,
|
||||
obj_name: obj_name.as_object().as_interned_str(vm).unwrap(),
|
||||
qualname: self.code.qualname,
|
||||
qualname: qualname.as_object().as_interned_str(vm).unwrap(),
|
||||
|
||||
max_stackdepth: self.code.max_stackdepth,
|
||||
instructions: self.code.instructions.clone(),
|
||||
max_stackdepth,
|
||||
instructions,
|
||||
locations: self.code.locations.clone(),
|
||||
constants: constants.into_iter().map(Literal).collect(),
|
||||
names: names
|
||||
@@ -431,12 +829,23 @@ impl PyCode {
|
||||
.into_iter()
|
||||
.map(|o| o.as_interned_str(vm).unwrap())
|
||||
.collect(),
|
||||
cellvars: self.code.cellvars.clone(),
|
||||
freevars: self.code.freevars.clone(),
|
||||
cellvars,
|
||||
freevars,
|
||||
cell2arg: self.code.cell2arg.clone(),
|
||||
linetable,
|
||||
exceptiontable,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn _varname_from_oparg(&self, opcode: i32, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
|
||||
let idx_err = |vm: &VirtualMachine| vm.new_index_error("tuple index out of range");
|
||||
|
||||
let idx = usize::try_from(opcode).map_err(|_| idx_err(vm))?;
|
||||
let name = self.code.varnames.get(idx).ok_or_else(|| idx_err(vm))?;
|
||||
Ok(name.to_object())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PyCode {
|
||||
@@ -457,6 +866,69 @@ impl ToPyObject for bytecode::CodeObject {
|
||||
}
|
||||
}
|
||||
|
||||
// Helper struct for reading linetable
|
||||
struct LineTableReader<'a> {
|
||||
data: &'a [u8],
|
||||
pos: usize,
|
||||
}
|
||||
|
||||
impl<'a> LineTableReader<'a> {
|
||||
fn new(data: &'a [u8]) -> Self {
|
||||
Self { data, pos: 0 }
|
||||
}
|
||||
|
||||
fn read_byte(&mut self) -> Option<u8> {
|
||||
if self.pos < self.data.len() {
|
||||
let byte = self.data[self.pos];
|
||||
self.pos += 1;
|
||||
Some(byte)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn peek_byte(&self) -> Option<u8> {
|
||||
if self.pos < self.data.len() {
|
||||
Some(self.data[self.pos])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn read_varint(&mut self) -> u32 {
|
||||
if let Some(first) = self.read_byte() {
|
||||
let mut val = (first & 0x3f) as u32;
|
||||
let mut shift = 0;
|
||||
let mut byte = first;
|
||||
while (byte & 0x40) != 0 {
|
||||
if let Some(next) = self.read_byte() {
|
||||
shift += 6;
|
||||
val |= ((next & 0x3f) as u32) << shift;
|
||||
byte = next;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
val
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
fn read_signed_varint(&mut self) -> i32 {
|
||||
let uval = self.read_varint();
|
||||
if uval & 1 != 0 {
|
||||
-((uval >> 1) as i32)
|
||||
} else {
|
||||
(uval >> 1) as i32
|
||||
}
|
||||
}
|
||||
|
||||
fn at_end(&self) -> bool {
|
||||
self.pos >= self.data.len()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(ctx: &Context) {
|
||||
PyCode::extend_class(ctx, ctx.types.code_type);
|
||||
}
|
||||
|
||||
73
wasm/demo/package-lock.json
generated
73
wasm/demo/package-lock.json
generated
@@ -21,7 +21,7 @@
|
||||
"css-loader": "^7.1.2",
|
||||
"html-webpack-plugin": "^5.6.3",
|
||||
"mini-css-extract-plugin": "^2.9.2",
|
||||
"serve": "^14.2.4",
|
||||
"serve": "^14.2.5",
|
||||
"webpack": "^5.97.1",
|
||||
"webpack-cli": "^6.0.1",
|
||||
"webpack-dev-server": "^5.2.1"
|
||||
@@ -1542,24 +1542,65 @@
|
||||
}
|
||||
},
|
||||
"node_modules/compression": {
|
||||
"version": "1.7.4",
|
||||
"resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz",
|
||||
"integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==",
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz",
|
||||
"integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.5",
|
||||
"bytes": "3.0.0",
|
||||
"compressible": "~2.0.16",
|
||||
"bytes": "3.1.2",
|
||||
"compressible": "~2.0.18",
|
||||
"debug": "2.6.9",
|
||||
"on-headers": "~1.0.2",
|
||||
"safe-buffer": "5.1.2",
|
||||
"negotiator": "~0.6.4",
|
||||
"on-headers": "~1.1.0",
|
||||
"safe-buffer": "5.2.1",
|
||||
"vary": "~1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/compression/node_modules/bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/compression/node_modules/negotiator": {
|
||||
"version": "0.6.4",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz",
|
||||
"integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/compression/node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
@@ -3540,9 +3581,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/on-headers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
|
||||
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
|
||||
"integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -4310,9 +4351,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/serve": {
|
||||
"version": "14.2.4",
|
||||
"resolved": "https://registry.npmjs.org/serve/-/serve-14.2.4.tgz",
|
||||
"integrity": "sha512-qy1S34PJ/fcY8gjVGszDB3EXiPSk5FKhUa7tQe0UPRddxRidc2V6cNHPNewbE1D7MAkgLuWEt3Vw56vYy73tzQ==",
|
||||
"version": "14.2.5",
|
||||
"resolved": "https://registry.npmjs.org/serve/-/serve-14.2.5.tgz",
|
||||
"integrity": "sha512-Qn/qMkzCcMFVPb60E/hQy+iRLpiU8PamOfOSYoAHmmF+fFFmpPpqa6Oci2iWYpTdOUM3VF+TINud7CfbQnsZbA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -4323,7 +4364,7 @@
|
||||
"chalk": "5.0.1",
|
||||
"chalk-template": "0.4.0",
|
||||
"clipboardy": "3.0.0",
|
||||
"compression": "1.7.4",
|
||||
"compression": "1.8.1",
|
||||
"is-port-reachable": "4.0.0",
|
||||
"serve-handler": "6.1.6",
|
||||
"update-check": "1.5.4"
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"css-loader": "^7.1.2",
|
||||
"html-webpack-plugin": "^5.6.3",
|
||||
"mini-css-extract-plugin": "^2.9.2",
|
||||
"serve": "^14.2.4",
|
||||
"serve": "^14.2.5",
|
||||
"webpack": "^5.97.1",
|
||||
"webpack-cli": "^6.0.1",
|
||||
"webpack-dev-server": "^5.2.1"
|
||||
|
||||
@@ -1,5 +1,2 @@
|
||||
[build]
|
||||
target = "wasm32-unknown-unknown"
|
||||
|
||||
[target.wasm32-unknown-unknown]
|
||||
rustflags = ["--cfg=getrandom_backend=\"wasm_js\""]
|
||||
|
||||
Reference in New Issue
Block a user