Compare commits

..

11 Commits

Author SHA1 Message Date
Jeong, YunWon
2b061612e5 Fix cron-ci failures: ctypes set_attr, missing features, __func__ AttributeError
- Use cls.set_attr() instead of cls.as_object().set_attr() in ctypes
  to ensure modified() is called and type cache stays valid
- Add host_env feature to cron-ci.yaml for frozen_origname_matches test
- Add stdio feature to cron-ci.yaml for encodings initialization
- Fix __func__ AttributeError in custom_text_test_runner.py
2026-03-19 22:13:28 +09:00
Jeong, YunWon
add34a2f19 Drop old PyObjectRef outside type lock to prevent deadlock
Dropping values inside with_type_lock can trigger weakref callbacks,
which may access attributes (LOAD_ATTR specialization) and re-acquire
the non-reentrant type mutex, causing deadlock.

Return old values from lock closures so they drop after lock release.
2026-03-19 22:13:28 +09:00
Jeong, YunWon
e0886e2fb6 type lock 2026-03-19 22:13:28 +09:00
Jeong, YunWon
328de2e83e Fix Constants newtype usage in init_cleanup_code 2026-03-19 22:13:28 +09:00
Jeong, YunWon
2d4e1f2f5e Extract datastack_frame_size_bytes_for_code, skip monitoring for init_cleanup frames, guard trace dispatch
- Extract datastack_frame_size_bytes_for_code as free function, use it
  to compute init_cleanup stack bytes instead of hardcoded constant
- Add monitoring_disabled_for_code to skip instrumentation for
  synthetic init_cleanup code object in RESUME and execute_instrumented
- Add is_trace_event guard so profile-only events skip trace_func dispatch
- Reformat core.rs (rustfmt)
2026-03-19 22:13:28 +09:00
Jeong, YunWon
15836ca0fc address review: check datastack space for extra_bytes, require CO_OPTIMIZED in vectorcall fast path 2026-03-19 22:13:28 +09:00
Jeong, YunWon
09c3bb1d7f address review: invalidate init cache on type modification, add cspell words 2026-03-19 22:13:28 +09:00
Jeong, YunWon
94e8d54731 Align call-init frame flow and spec cache atomic ordering 2026-03-19 22:13:28 +09:00
Jeong, YunWon
382be9a525 Tighten CALL_ALLOC_AND_ENTER_INIT stack-space guard 2026-03-19 22:13:28 +09:00
Jeong, YunWon
77b46d53ca Align type _spec_cache and latin1 singleton string paths 2026-03-19 22:13:28 +09:00
Jeong, YunWon
bcd618ecc9 Align BINARY_OP_EXTEND with CPython descriptor cache model 2026-03-19 22:13:28 +09:00
20 changed files with 214 additions and 197 deletions

View File

@@ -141,12 +141,7 @@ jobs:
- name: Install dependencies
uses: ./.github/actions/install-linux-deps
# zizmor has an issue with dynamic `with`
# with: ${{ matrix.dependencies || fromJSON('{}') }}
with:
gcc-multilib: ${{ matrix.dependencies.gcc-multilib || false }}
musl-tools: ${{ matrix.dependencies.musl-tools || false }}
gcc-aarch64-linux-gnu: ${{ matrix.dependencies.gcc-aarch64-linux-gnu || false }}
with: ${{ matrix.dependencies || fromJSON('{}') }}
- uses: dtolnay/rust-toolchain@stable
with:
@@ -185,13 +180,76 @@ jobs:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'skip:ci') }}
env:
RUST_BACKTRACE: full
# Tests that can be flaky when running with multiple processes `-j 2`. We will use `-j 1` for these.
FLAKY_MP_TESTS: >-
test_class
test_eintr
test_multiprocessing_fork
test_multiprocessing_forkserver
test_multiprocessing_spawn
# PLATFORM_INDEPENDENT_TESTS are tests that do not depend on the underlying OS.
# They are currently only run on Linux to speed up the CI.
PLATFORM_INDEPENDENT_TESTS: >-
test__colorize
test_array
test_asyncgen
test_binop
test_bisect
test_bool
test_bytes
test_call
test_cmath
test_collections
test_complex
test_contains
test_copy
test_dataclasses
test_decimal
test_decorators
test_defaultdict
test_deque
test_dict
test_dictcomps
test_dictviews
test_dis
test_enumerate
test_exception_variations
test_float
test_fractions
test_genericalias
test_genericclass
test_grammar
test_range
test_index
test_int
test_int_literal
test_isinstance
test_iter
test_iterlen
test_itertools
test_json
test_keyword
test_keywordonlyarg
test_list
test_long
test_longexp
test_operator
test_ordered_dict
test_pep646_syntax
test_pow
test_raise
test_richcmp
test_scope
test_set
test_slice
test_sort
test_string
test_string_literals
test_strtod
test_structseq
test_subclassinit
test_super
test_syntax
test_tstring
test_tuple
test_unary
test_unpack
test_unpack_ex
test_weakref
test_yield_from
name: Run snippets and cpython tests
runs-on: ${{ matrix.os }}
strategy:
@@ -246,23 +304,17 @@ jobs:
run: python -m pip install -r requirements.txt && pytest -v
working-directory: ./extra_tests
- name: Detect available cores
id: cores
shell: bash
- name: run cpython platform-independent tests
if: runner.os == 'Linux'
run: |
cores=$(python -c 'print(__import__("os").process_cpu_count())')
echo "cores=${cores}" >> $GITHUB_OUTPUT
- name: Run CPython tests
run: |
target/release/rustpython -m test -j ${{ steps.cores.outputs.cores }} ${{ join(matrix.extra_test_args, ' ') }} --slowest --fail-env-changed --timeout 600 -v -x ${{ env.FLAKY_MP_TESTS }} ${{ join(matrix.skips, ' ') }}
timeout-minutes: ${{ matrix.timeout }}
target/release/rustpython -m test -j 1 -u all --slowest --fail-env-changed --timeout 600 -v ${{ env.PLATFORM_INDEPENDENT_TESTS }}
timeout-minutes: 45
env:
RUSTPYTHON_SKIP_ENV_POLLUTERS: true
- name: Run flaky MP CPython tests
- name: run cpython platform-dependent tests
run: |
target/release/rustpython -m test -j 1 ${{ join(matrix.extra_test_args, ' ') }} --slowest --fail-env-changed --timeout 600 -v ${{ env.FLAKY_MP_TESTS }}
target/release/rustpython -m test -j 1 ${{ join(matrix.extra_test_args, ' ') }} --slowest --fail-env-changed --timeout 600 -v -x ${{ env.PLATFORM_INDEPENDENT_TESTS }} ${{ join(matrix.skips, ' ') }}
timeout-minutes: ${{ matrix.timeout }}
env:
RUSTPYTHON_SKIP_ENV_POLLUTERS: true
@@ -505,29 +557,3 @@ jobs:
run: wasmer run --dir $(pwd) target/wasm32-wasip1/release/rustpython.wasm -- "$(pwd)/extra_tests/snippets/stdlib_random.py"
- name: run cpython unittest
run: wasmer run --dir $(pwd) target/wasm32-wasip1/release/rustpython.wasm -- "$(pwd)/Lib/test/test_int.py"
cargo-shear:
name: cargo shear
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: cargo-bins/cargo-binstall@1800853f2578f8c34492ec76154caef8e163fbca # v1.17.7
- run: cargo binstall --no-confirm cargo-shear
- run: cargo shear
security-lint:
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Run zizmor
uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2

View File

@@ -35,7 +35,7 @@ jobs:
python-version: ${{ env.PYTHON_VERSION }}
- run: sudo apt-get update && sudo apt-get -y install lcov
- name: Run cargo-llvm-cov with Rust tests.
run: cargo llvm-cov --no-report --workspace --exclude rustpython_wasm --exclude rustpython-compiler-source --exclude rustpython-venvlauncher --verbose --no-default-features --features stdlib,importlib,stdio,encodings,ssl-rustls,jit,host_env
run: cargo llvm-cov --no-report --workspace --exclude rustpython_wasm --exclude rustpython-compiler-source --exclude rustpython-venvlauncher --verbose --no-default-features --features stdlib,importlib,stdio,encodings,ssl-rustls,jit
- name: Run cargo-llvm-cov with Python snippets.
run: python scripts/cargo-llvm-cov.py
continue-on-error: true
@@ -48,7 +48,7 @@ jobs:
if: ${{ github.event_name != 'pull_request' }}
uses: codecov/codecov-action@v5
with:
files: ./codecov.lcov
file: ./codecov.lcov
testdata:
name: Collect regression test data
@@ -170,12 +170,12 @@ jobs:
- name: restructure generated files
run: |
cd ./target/criterion/reports
find . -type d -name cpython -print0 | xargs -0 rm -rf
find . -type d -name rustpython -print0 | xargs -0 rm -rf
find . -mindepth 2 -maxdepth 2 -name violin.svg -print0 | xargs -0 rm -rf
find . -type f -not -name violin.svg -print0 | xargs -0 rm -rf
find . -type f -name violin.svg -exec sh -c 'for file; do mv "$file" "$(echo "$file" | sed -E "s_\./([^/]+)/([^/]+)/violin\.svg_./\1/\2.svg_")"; done' _ {} +
find . -mindepth 2 -maxdepth 2 -type d -print0 | xargs -0 rm -rf
find -type d -name cpython | xargs rm -rf
find -type d -name rustpython | xargs rm -rf
find -mindepth 2 -maxdepth 2 -name violin.svg | xargs rm -rf
find -type f -not -name violin.svg | xargs rm -rf
for file in $(find -type f -name violin.svg); do mv $file $(echo $file | sed -E "s_\./([^/]+)/([^/]+)/violin\.svg_./\1/\2.svg_"); done
find -mindepth 2 -maxdepth 2 -type d | xargs rm -rf
cd ..
mv reports/* .
rmdir reports

55
Cargo.lock generated
View File

@@ -113,7 +113,7 @@ version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
dependencies = [
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -124,7 +124,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
dependencies = [
"anstyle",
"once_cell_polyfill",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -1140,6 +1140,12 @@ dependencies = [
"regex",
]
[[package]]
name = "env_home"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe"
[[package]]
name = "env_logger"
version = "0.11.9"
@@ -1166,7 +1172,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -1664,7 +1670,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cfc352a66ba903c23239ef51e809508b6fc2b0f90e3476ac7a9ff47e863ae95"
dependencies = [
"scopeguard",
"windows-sys 0.61.2",
"windows-sys 0.59.0",
]
[[package]]
@@ -2976,7 +2982,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -3041,7 +3047,7 @@ dependencies = [
"security-framework",
"security-framework-sys",
"webpki-root-certs",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -3293,6 +3299,7 @@ dependencies = [
"mt19937",
"nix 0.30.1",
"num-complex",
"num-integer",
"num-traits",
"num_enum",
"oid-registry",
@@ -3335,7 +3342,9 @@ dependencies = [
"unic-ucd-age",
"unic-ucd-bidi",
"unic-ucd-category",
"unic-ucd-ident",
"unicode-bidi-mirroring",
"unicode-casing",
"unicode_names2 2.0.0",
"uuid",
"webpki-roots",
@@ -3349,6 +3358,9 @@ dependencies = [
[[package]]
name = "rustpython-venvlauncher"
version = "0.5.0"
dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "rustpython-vm"
@@ -3412,6 +3424,7 @@ dependencies = [
"strum",
"strum_macros",
"thiserror 2.0.18",
"thread_local",
"timsort",
"uname",
"unic-ucd-bidi",
@@ -3440,6 +3453,7 @@ version = "0.5.0"
dependencies = [
"console_error_panic_hook",
"js-sys",
"ruff_python_parser",
"rustpython-common",
"rustpython-pylib",
"rustpython-stdlib",
@@ -3733,7 +3747,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
dependencies = [
"libc",
"windows-sys 0.61.2",
"windows-sys 0.60.2",
]
[[package]]
@@ -3861,7 +3875,7 @@ dependencies = [
"getrandom 0.3.4",
"once_cell",
"rustix",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -3930,6 +3944,15 @@ dependencies = [
"winapi",
]
[[package]]
name = "thread_local"
version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
dependencies = [
"cfg-if",
]
[[package]]
name = "time"
version = "0.3.47"
@@ -4450,11 +4473,13 @@ dependencies = [
[[package]]
name = "which"
version = "8.0.2"
version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81995fafaaaf6ae47a7d0cc83c67caf92aeb7e5331650ae6ff856f7c0c60c459"
checksum = "d3fabb953106c3c8eea8306e4393700d7657561cb43122571b172bbfb7c7ba1d"
dependencies = [
"libc",
"env_home",
"rustix",
"winsafe",
]
[[package]]
@@ -4495,7 +4520,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@@ -4810,6 +4835,12 @@ dependencies = [
"version_check",
]
[[package]]
name = "winsafe"
version = "0.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
[[package]]
name = "wit-bindgen"
version = "0.51.0"

View File

@@ -35,6 +35,7 @@ rustpython-compiler = { workspace = true }
rustpython-pylib = { workspace = true, optional = true }
rustpython-stdlib = { workspace = true, optional = true, features = ["compiler"] }
rustpython-vm = { workspace = true, features = ["compiler", "gc"] }
ruff_python_parser = { workspace = true }
cfg-if = { workspace = true }
log = { workspace = true }
@@ -55,7 +56,6 @@ rustyline = { workspace = true }
criterion = { workspace = true }
pyo3 = { version = "0.28.2", features = ["auto-initialize"] }
rustpython-stdlib = { workspace = true }
ruff_python_parser = { workspace = true }
[[bench]]
name = "execution"
@@ -169,6 +169,7 @@ ascii = "1.1"
bitflags = "2.11.0"
bitflagset = "0.0.3"
bstr = "1"
bytes = "1.11.1"
cfg-if = "1.0"
chrono = { version = "0.4.44", default-features = false, features = ["clock", "oldtime", "std"] }
constant_time_eq = "0.4"
@@ -215,6 +216,7 @@ strum = "0.27"
strum_macros = "0.28"
syn = "2"
thiserror = "2.0"
thread_local = "1.1.9"
unicode-casing = "0.1.1"
unic-char-property = "0.9.0"
unic-normal = "0.9.0"

View File

@@ -392,6 +392,7 @@ class SocketEINTRTest(EINTRBaseTest):
class TimeEINTRTest(EINTRBaseTest):
""" EINTR tests for the time module. """
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_sleep(self):
t0 = time.monotonic()
time.sleep(self.sleep_time)

View File

@@ -1,6 +1,5 @@
"Test the functionality of Python classes implementing operators."
import sys
import unittest
from test import support
from test.support import cpython_only, import_helper, script_helper
@@ -615,7 +614,7 @@ class ClassTests(unittest.TestCase):
with self.assertRaises(TypeError):
a >= b
@unittest.skipIf(sys.platform == "win32", "TODO: RUSTPYTHON; flaky on Windows")
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; AssertionError: 1543448294720 != 1543448295392")
def testHashComparisonOfMethods(self):
# Test comparison and hash of methods
class A:

View File

@@ -153,6 +153,7 @@ class TestBase(unittest.TestCase):
class TestBugs(unittest.TestCase):
@unittest.skip("TODO: RUSTPYTHON; figure out how to detect sort mutation that doesn't change list length")
def test_bug453523(self):
# bug 453523 -- list.sort() crasher.
# If this fails, the most likely outcome is a core dump.
@@ -169,6 +170,7 @@ class TestBugs(unittest.TestCase):
L = [C() for i in range(50)]
self.assertRaises(ValueError, L.sort)
@unittest.expectedFailure # TODO: RUSTPYTHON; figure out how to detect sort mutation that doesn't change list length
def test_undetected_mutation(self):
# Python 2.4a1 did not always detect mutation
memorywaster = []

View File

@@ -6881,9 +6881,9 @@ impl Compiler {
self,
Instruction::Resume {
context: if is_await {
bytecode::ResumeType::AfterAwait
u32::from(bytecode::ResumeType::AfterAwait)
} else {
bytecode::ResumeType::AfterYieldFrom
u32::from(bytecode::ResumeType::AfterYieldFrom)
}
}
);
@@ -7055,7 +7055,7 @@ impl Compiler {
emit!(
self,
Instruction::Resume {
context: bytecode::ResumeType::AfterYield
context: u32::from(bytecode::ResumeType::AfterYield)
}
);
}
@@ -7277,7 +7277,7 @@ impl Compiler {
emit!(
compiler,
Instruction::Resume {
context: bytecode::ResumeType::AfterYield
context: u32::from(bytecode::ResumeType::AfterYield)
}
);
emit!(compiler, Instruction::PopTop);

View File

@@ -304,7 +304,7 @@ pub enum Instruction {
} = 120,
// CPython 3.14 RESUME (128)
Resume {
context: Arg<oparg::ResumeType>,
context: Arg<u32>,
} = 128,
// CPython 3.14 specialized opcodes (129-211)
BinaryOpAddFloat = 129, // Placeholder

View File

@@ -276,47 +276,16 @@ impl fmt::Display for ConvertValueOparg {
}
}
/// Resume type for the RESUME instruction
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub enum ResumeType {
AtFuncStart,
AfterYield,
AfterYieldFrom,
AfterAwait,
Other(u32),
}
impl From<u32> for ResumeType {
fn from(value: u32) -> Self {
match value {
0 => Self::AtFuncStart,
1 => Self::AfterYield,
2 => Self::AfterYieldFrom,
3 => Self::AfterAwait,
_ => Self::Other(value),
}
oparg_enum!(
/// Resume type for the RESUME instruction
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub enum ResumeType {
AtFuncStart = 0,
AfterYield = 1,
AfterYieldFrom = 2,
AfterAwait = 3,
}
}
impl From<ResumeType> for u32 {
fn from(typ: ResumeType) -> Self {
match typ {
ResumeType::AtFuncStart => 0,
ResumeType::AfterYield => 1,
ResumeType::AfterYieldFrom => 2,
ResumeType::AfterAwait => 3,
ResumeType::Other(v) => v,
}
}
}
impl core::fmt::Display for ResumeType {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
u32::from(*self).fmt(f)
}
}
impl OpArgType for ResumeType {}
);
pub type NameIdx = u32;
@@ -745,7 +714,6 @@ macro_rules! newtype_oparg {
self.0.fmt(f)
}
}
impl OpArgType for $name {}
}
}

View File

@@ -48,6 +48,7 @@ libc = { workspace = true }
nix = { workspace = true }
num-complex = { workspace = true }
malachite-bigint = { workspace = true }
num-integer = { workspace = true }
num-traits = { workspace = true }
num_enum = { workspace = true }
parking_lot = { workspace = true }
@@ -77,12 +78,16 @@ constant_time_eq = { workspace = true }
## 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 }
# update version all at the same time
unic-char-property = { workspace = true }
unic-normal = { workspace = true }
unic-ucd-bidi = { workspace = true }
unic-ucd-category = { workspace = true }
unic-ucd-age = { workspace = true }
unic-ucd-ident = { workspace = true }
ucd = "0.1.1"
unicode-bidi-mirroring = { workspace = true }

View File

@@ -193,8 +193,7 @@ mod _multiprocessing {
remaining.min(poll_ms)
};
let handle = self.handle.as_raw();
let res = vm.allow_threads(|| unsafe { WaitForSingleObjectEx(handle, wait_ms, 0) });
let res = unsafe { WaitForSingleObjectEx(self.handle.as_raw(), wait_ms, 0) };
match res {
WAIT_OBJECT_0 => {

View File

@@ -13,5 +13,15 @@ license.workspace = true
name = "venvlaunchert"
path = "src/main.rs"
[target.'cfg(windows)'.dependencies]
windows-sys = { workspace = true, features = [
"Win32_Foundation",
"Win32_System_Threading",
"Win32_System_Environment",
"Win32_Storage_FileSystem",
"Win32_System_Console",
"Win32_Security",
] }
[lints]
workspace = true

View File

@@ -72,6 +72,7 @@ static_assertions = { workspace = true }
strum = { workspace = true }
strum_macros = { workspace = true }
thiserror = { workspace = true }
thread_local = { workspace = true }
memchr = { workspace = true }
caseless = "0.2.2"

View File

@@ -5,7 +5,6 @@ use super::{
PyAsyncGen, PyCode, PyCoroutine, PyDictRef, PyGenerator, PyModule, PyStr, PyStrRef, PyTuple,
PyTupleRef, PyType,
};
use crate::common::hash::PyHash;
use crate::common::lock::PyMutex;
use crate::function::ArgMapping;
use crate::object::{PyAtomicRef, Traverse, TraverseFn};
@@ -18,8 +17,7 @@ use crate::{
function::{FuncArgs, OptionalArg, PyComparisonValue, PySetterValue},
scope::Scope,
types::{
Callable, Comparable, Constructor, GetAttr, GetDescriptor, Hashable, PyComparisonOp,
Representable,
Callable, Comparable, Constructor, GetAttr, GetDescriptor, PyComparisonOp, Representable,
},
};
use core::sync::atomic::{AtomicU32, Ordering::Relaxed};
@@ -1195,14 +1193,6 @@ impl Comparable for PyBoundMethod {
}
}
impl Hashable for PyBoundMethod {
fn hash(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyHash> {
let self_hash = crate::common::hash::hash_object_id_raw(zelf.object.get_id());
let func_hash = zelf.function.hash(vm)?;
Ok(crate::common::hash::fix_sentinel(self_hash ^ func_hash))
}
}
impl GetAttr for PyBoundMethod {
fn getattro(zelf: &Py<Self>, name: &Py<PyStr>, vm: &VirtualMachine) -> PyResult {
let class_attr = vm
@@ -1258,7 +1248,7 @@ impl PyBoundMethod {
}
#[pyclass(
with(Callable, Comparable, Hashable, GetAttr, Constructor, Representable),
with(Callable, Comparable, GetAttr, Constructor, Representable),
flags(IMMUTABLETYPE, HAS_WEAKREF)
)]
impl PyBoundMethod {

View File

@@ -24,7 +24,7 @@ use core::cell::Cell;
use core::ops::{Neg, Not};
use core::ptr::NonNull;
use malachite_bigint::{BigInt, Sign};
use num_integer::{ExtendedGcd, Integer};
use num_integer::Integer;
use num_traits::{One, Pow, PrimInt, Signed, ToPrimitive, Zero};
#[pyclass(module = false, name = "int")]
@@ -414,6 +414,7 @@ impl PyInt {
if a.is_negative() { a + n } else { a }
}
fn inverse(a: BigInt, n: &BigInt) -> Option<BigInt> {
use num_integer::*;
let ExtendedGcd { gcd, x: c, .. } = a.extended_gcd(n);
if gcd.is_one() {
Some(normalize(c, n))

View File

@@ -30,13 +30,11 @@ use alloc::fmt;
use core::cell::Cell;
use core::ops::DerefMut;
use core::ptr::NonNull;
use core::sync::atomic::{AtomicU32, Ordering};
#[pyclass(module = false, name = "list", unhashable = true, traverse = "manual")]
#[derive(Default)]
pub struct PyList {
elements: PyRwLock<Vec<PyObjectRef>>,
mutation_counter: AtomicU32,
}
impl fmt::Debug for PyList {
@@ -50,7 +48,6 @@ impl From<Vec<PyObjectRef>> for PyList {
fn from(elements: Vec<PyObjectRef>) -> Self {
Self {
elements: PyRwLock::new(elements),
mutation_counter: AtomicU32::new(0),
}
}
}
@@ -138,9 +135,7 @@ impl PyList {
}
pub fn borrow_vec_mut(&self) -> PyRwLockWriteGuard<'_, Vec<PyObjectRef>> {
let guard = self.elements.write();
self.mutation_counter.fetch_add(1, Ordering::Relaxed);
guard
self.elements.write()
}
fn repeat(&self, n: isize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
@@ -396,21 +391,12 @@ impl PyList {
// replace list contents with [] for duration of sort.
// this prevents keyfunc from messing with the list and makes it easy to
// check if it tries to append elements to it.
let (mut elements, version_before) = {
let mut guard = self.elements.write();
let version_before = self.mutation_counter.load(Ordering::Relaxed);
(core::mem::take(guard.deref_mut()), version_before)
};
let mut elements = core::mem::take(self.borrow_vec_mut().deref_mut());
let res = do_sort(vm, &mut elements, options.key, options.reverse);
let mutated = {
let mut guard = self.elements.write();
let mutated = self.mutation_counter.load(Ordering::Relaxed) != version_before;
core::mem::swap(guard.deref_mut(), &mut elements);
mutated
};
core::mem::swap(self.borrow_vec_mut().deref_mut(), &mut elements);
res?;
if mutated {
if !elements.is_empty() {
return Err(vm.new_value_error("list modified during sort"));
}

View File

@@ -115,30 +115,17 @@ mod decl {
#[cfg(unix)]
{
// Loop on nanosleep, recomputing the
// remaining timeout after each EINTR so that signals don't
// shorten the requested sleep duration.
use std::time::Instant;
let deadline = Instant::now() + dur;
loop {
let remaining = deadline.saturating_duration_since(Instant::now());
if remaining.is_zero() {
break;
}
let ts = nix::sys::time::TimeSpec::from(remaining);
let (res, err) = vm.allow_threads(|| {
let r = unsafe { libc::nanosleep(ts.as_ref(), core::ptr::null_mut()) };
(r, nix::Error::last_raw())
});
if res == 0 {
break;
}
if err != libc::EINTR {
return Err(
vm.new_os_error(format!("nanosleep: {}", nix::Error::from_raw(err)))
);
}
// EINTR: run signal handlers, then retry with remaining time
// this is basically std::thread::sleep, but that catches interrupts and we don't want to;
let ts = nix::sys::time::TimeSpec::from(dur);
// Capture errno inside the closure: attach_thread (called by
// allow_threads on return) can clobber errno via syscalls.
let (res, err) = vm.allow_threads(|| {
let r = unsafe { libc::nanosleep(ts.as_ref(), core::ptr::null_mut()) };
(r, nix::Error::last_raw())
});
let interrupted = res == -1 && err == libc::EINTR;
if interrupted {
vm.check_signals()?;
}
}

View File

@@ -23,6 +23,8 @@ rustpython-stdlib = { workspace = true, default-features = false, optional = tru
# make sure no threading! otherwise wasm build will fail
rustpython-vm = { workspace = true, features = ["compiler", "encodings", "serde", "wasmbind"] }
ruff_python_parser = { workspace = true }
serde = { workspace = true }
wasm-bindgen = { workspace = true }
@@ -46,6 +48,3 @@ wasm-opt = false#["-O1"]
[lints]
workspace = true
[package.metadata.cargo-shear]
ignored = ["serde", "rustpython-common"]

View File

@@ -44,7 +44,7 @@ def _get_method_dict(test):
Works for both bound methods (__func__.__dict__) and plain functions.
"""
method = getattr(test, test._testMethodName)
func = getattr(method, "__func__", method)
func = getattr(method, '__func__', method)
return func.__dict__
@@ -335,7 +335,9 @@ class CustomTextTestResult(result.TestResult):
self.stream.writeln("TEST SUITE: %s" % self.suite)
self.stream.writeln("Description: %s" % self.getSuiteDescription(test))
try:
name_override = _get_method_dict(test)["test_case_name"]
name_override = _get_method_dict(test)[
"test_case_name"
]
except:
name_override = None
self.case = name_override if name_override else self.case
@@ -353,11 +355,7 @@ class CustomTextTestResult(result.TestResult):
self.results["suites"][self.suite_number] = {
"name": self.suite,
"class": test.__class__.__name__,
"module": (
m.group(1)
if (m := re.compile(r".* \((.*)\)").match(str(test)))
else str(test)
),
"module": re.compile(".* \((.*)\)").match(str(test)).group(1),
"description": self.getSuiteDescription(test),
"cases": {},
"used_case_names": {},
@@ -392,22 +390,34 @@ class CustomTextTestResult(result.TestResult):
if "test_type" in getattr(
test, test._testMethodName
).__func__.__dict__ and set([s.lower() for s in self.test_types]) == set(
[s.lower() for s in _get_method_dict(test)["test_type"]]
[
s.lower()
for s in _get_method_dict(test)[
"test_type"
]
]
):
pass
else:
_get_method_dict(test)["__unittest_skip_why__"] = (
'Test run specified to only run tests of type "%s"'
% ",".join(self.test_types)
_get_method_dict(test)[
"__unittest_skip_why__"
] = 'Test run specified to only run tests of type "%s"' % ",".join(
self.test_types
)
_get_method_dict(test)["__unittest_skip__"] = True
_get_method_dict(test)[
"__unittest_skip__"
] = True
if "skip_device" in _get_method_dict(test):
for device in _get_method_dict(test)["skip_device"]:
for device in _get_method_dict(test)[
"skip_device"
]:
if self.config and device.lower() in self.config["device_name"].lower():
_get_method_dict(test)["__unittest_skip_why__"] = (
"Test is marked to be skipped on %s" % device
)
_get_method_dict(test)["__unittest_skip__"] = True
_get_method_dict(test)[
"__unittest_skip_why__"
] = "Test is marked to be skipped on %s" % device
_get_method_dict(test)[
"__unittest_skip__"
] = True
break
def stopTest(self, test):