forked from Rust-related/RustPython
Compare commits
34 Commits
update_tes
...
2025-01-13
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7a7b6b923 | ||
|
|
0e00d2328d | ||
|
|
53db70e784 | ||
|
|
76c699b4ba | ||
|
|
c901bc07a4 | ||
|
|
b7db23bbae | ||
|
|
389b20d977 | ||
|
|
d06459fa49 | ||
|
|
e2a55cbf34 | ||
|
|
a5e6ade9cb | ||
|
|
a1e32566d3 | ||
|
|
8c7bfb3e1a | ||
|
|
bb0480e978 | ||
|
|
2ccc745513 | ||
|
|
bea83fe94d | ||
|
|
3feaf689d8 | ||
|
|
bd627b58af | ||
|
|
c561d33cb2 | ||
|
|
c8fd3bd683 | ||
|
|
fef1e31634 | ||
|
|
1abaf87abe | ||
|
|
38593fbd85 | ||
|
|
8d187fd275 | ||
|
|
646cc81656 | ||
|
|
01f7536b36 | ||
|
|
97e5ec02f8 | ||
|
|
3dced01af0 | ||
| 0cf4534c5c | |||
| 044f66fba3 | |||
| 40a9ddad4e | |||
|
|
8ac7e34be2 | ||
|
|
c883f0ad8a | ||
|
|
eae60113af | ||
|
|
1aab5240cf |
9
.github/workflows/ci.yaml
vendored
9
.github/workflows/ci.yaml
vendored
@@ -229,6 +229,7 @@ jobs:
|
||||
uses: coolreader18/redoxer-action@v1
|
||||
with:
|
||||
command: check
|
||||
args: --ignore-rust-version
|
||||
|
||||
snippets_cpython:
|
||||
if: ${{ !contains(github.event.pull_request.labels.*.name, 'skip:ci') }}
|
||||
@@ -407,7 +408,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
target: wasm32-wasi
|
||||
target: wasm32-wasip1
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Setup Wasmer
|
||||
@@ -415,8 +416,8 @@ jobs:
|
||||
- name: Install clang
|
||||
run: sudo apt-get update && sudo apt-get install clang -y
|
||||
- name: build rustpython
|
||||
run: cargo build --release --target wasm32-wasi --features freeze-stdlib,stdlib --verbose
|
||||
run: cargo build --release --target wasm32-wasip1 --features freeze-stdlib,stdlib --verbose
|
||||
- name: run snippets
|
||||
run: wasmer run --dir `pwd` target/wasm32-wasi/release/rustpython.wasm -- `pwd`/extra_tests/snippets/stdlib_random.py
|
||||
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-wasi/release/rustpython.wasm -- `pwd`/Lib/test/test_int.py
|
||||
run: wasmer run --dir `pwd` target/wasm32-wasip1/release/rustpython.wasm -- `pwd`/Lib/test/test_int.py
|
||||
|
||||
145
.github/workflows/release.yml
vendored
Normal file
145
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# 9 AM UTC on every Monday
|
||||
- cron: "0 9 * * Mon"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pre-release:
|
||||
type: boolean
|
||||
description: Mark "Pre-Release"
|
||||
required: false
|
||||
default: true
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
CARGO_ARGS: --no-default-features --features stdlib,zlib,importlib,encodings,sqlite,ssl
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.platform.runner }}
|
||||
strategy:
|
||||
matrix:
|
||||
platform:
|
||||
- runner: ubuntu-latest
|
||||
target: x86_64-unknown-linux-gnu
|
||||
# - runner: ubuntu-latest
|
||||
# target: i686-unknown-linux-gnu
|
||||
# - runner: ubuntu-latest
|
||||
# target: aarch64-unknown-linux-gnu
|
||||
# - runner: ubuntu-latest
|
||||
# target: armv7-unknown-linux-gnueabi
|
||||
# - runner: ubuntu-latest
|
||||
# target: s390x-unknown-linux-gnu
|
||||
# - runner: ubuntu-latest
|
||||
# target: powerpc64le-unknown-linux-gnu
|
||||
- runner: macos-latest
|
||||
target: aarch64-apple-darwin
|
||||
# - runner: macos-latest
|
||||
# target: x86_64-apple-darwin
|
||||
- runner: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
# - runner: windows-latest
|
||||
# target: i686-pc-windows-msvc
|
||||
# - runner: windows-latest
|
||||
# target: aarch64-pc-windows-msvc
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Set up Environment
|
||||
shell: bash
|
||||
run: rustup target add ${{ matrix.platform.target }}
|
||||
- name: Set up Windows Environment
|
||||
shell: bash
|
||||
run: |
|
||||
cargo install --target-dir=target -v cargo-vcpkg
|
||||
cargo vcpkg -v build
|
||||
if: runner.os == 'Windows'
|
||||
- name: Set up MacOS Environment
|
||||
run: brew install autoconf automake libtool
|
||||
if: runner.os == 'macOS'
|
||||
|
||||
- name: Build RustPython
|
||||
run: cargo build --release --target=${{ matrix.platform.target }} --verbose --features=threading ${{ env.CARGO_ARGS }}
|
||||
if: runner.os == 'macOS'
|
||||
- name: Build RustPython
|
||||
run: cargo build --release --target=${{ matrix.platform.target }} --verbose --features=threading ${{ env.CARGO_ARGS }},jit
|
||||
if: runner.os != 'macOS'
|
||||
|
||||
- name: Rename Binary
|
||||
run: cp target/${{ matrix.platform.target }}/release/rustpython target/rustpython-release-${{ runner.os }}-${{ matrix.platform.target }}
|
||||
if: runner.os != 'Windows'
|
||||
- name: Rename Binary
|
||||
run: cp target/${{ matrix.platform.target }}/release/rustpython.exe target/rustpython-release-${{ runner.os }}-${{ matrix.platform.target }}.exe
|
||||
if: runner.os == 'Windows'
|
||||
|
||||
- name: Upload Binary Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rustpython-release-${{ runner.os }}-${{ matrix.platform.target }}
|
||||
path: target/rustpython-release-${{ runner.os }}-${{ matrix.platform.target }}*
|
||||
|
||||
build-wasm:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Set up Environment
|
||||
shell: bash
|
||||
run: rustup target add wasm32-wasip1
|
||||
|
||||
- name: Build RustPython
|
||||
run: cargo build --target wasm32-wasip1 --no-default-features --features freeze-stdlib,stdlib --release
|
||||
|
||||
- name: Rename Binary
|
||||
run: cp target/wasm32-wasip1/release/rustpython.wasm target/rustpython-release-wasm32-wasip1.wasm
|
||||
|
||||
- name: Upload Binary Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rustpython-release-wasm32-wasip1
|
||||
path: target/rustpython-release-wasm32-wasip1.wasm
|
||||
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build, build-wasm]
|
||||
steps:
|
||||
- name: Download Binary Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: bin
|
||||
pattern: rustpython-release-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: List Binaries
|
||||
run: |
|
||||
ls -lah bin/
|
||||
file bin/*
|
||||
- name: Create Release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag: ${{ github.ref_name }}
|
||||
run: ${{ github.run_number }}
|
||||
run: |
|
||||
if [[ "${{ github.event.inputs.pre-release }}" == "false" ]]; then
|
||||
RELEASE_TYPE_NAME=Release
|
||||
PRERELEASE_ARG=
|
||||
else
|
||||
RELEASE_TYPE_NAME=Pre-Release
|
||||
PRERELEASE_ARG=--prerelease
|
||||
fi
|
||||
|
||||
today=$(date '+%Y-%m-%d')
|
||||
gh release create "$today-$tag-$run" \
|
||||
--repo="$GITHUB_REPOSITORY" \
|
||||
--title="RustPython $RELEASE_TYPE_NAME $today-$tag #$run" \
|
||||
--target="$tag" \
|
||||
--generate-notes \
|
||||
$PRERELEASE_ARG \
|
||||
bin/rustpython-release-*
|
||||
43
Cargo.lock
generated
43
Cargo.lock
generated
@@ -207,11 +207,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "caseless"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "808dab3318747be122cb31d36de18d4d1c81277a76f8332a02b81a3d73463d7f"
|
||||
checksum = "8b6fd507454086c8edfd769ca6ada439193cdb209c7681712ef6275cccbfe5d8"
|
||||
dependencies = [
|
||||
"regex",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
@@ -1566,13 +1565,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pmutil"
|
||||
version = "0.5.3"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3894e5d549cccbe44afecf72922f277f603cd4bb0219c8342631ef18fffbe004"
|
||||
checksum = "52a40bc70c2c58040d2d8b167ba9a5ff59fc9dab7ad44771cfde3dcfde7a09c6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.77",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1824,24 +1823,23 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "result-like"
|
||||
version = "0.4.6"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccc7ce6435c33898517a30e85578cd204cbb696875efb93dec19a2d31294f810"
|
||||
checksum = "abf7172fef6a7d056b5c26bf6c826570267562d51697f4982ff3ba4aec68a9df"
|
||||
dependencies = [
|
||||
"result-like-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "result-like-derive"
|
||||
version = "0.4.6"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fabf0a2e54f711c68c50d49f648a1a8a37adcb57353f518ac4df374f0788f42"
|
||||
checksum = "a8d6574c02e894d66370cfc681e5d68fedbc9a548fb55b30a96b3f0ae22d0fe5"
|
||||
dependencies = [
|
||||
"pmutil",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"syn-ext",
|
||||
"syn 2.0.77",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2948,9 +2946,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.93"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
|
||||
checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
@@ -2959,13 +2957,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.93"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
|
||||
checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
@@ -2986,9 +2983,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.93"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
|
||||
checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -2996,9 +2993,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.93"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
|
||||
checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3009,9 +3006,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.93"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
|
||||
checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
|
||||
@@ -104,7 +104,7 @@ members = [
|
||||
version = "0.4.0"
|
||||
authors = ["RustPython Team"]
|
||||
edition = "2021"
|
||||
rust-version = "1.80.0"
|
||||
rust-version = "1.83.0"
|
||||
repository = "https://github.com/RustPython/RustPython"
|
||||
license = "MIT"
|
||||
|
||||
|
||||
22
Lib/_dummy_os.py
vendored
22
Lib/_dummy_os.py
vendored
@@ -5,22 +5,30 @@ A shim of the os module containing only simple path-related utilities
|
||||
try:
|
||||
from os import *
|
||||
except ImportError:
|
||||
import abc
|
||||
import abc, sys
|
||||
|
||||
def __getattr__(name):
|
||||
raise OSError("no os specific module found")
|
||||
if name in {"_path_normpath", "__path__"}:
|
||||
raise AttributeError(name)
|
||||
if name.isupper():
|
||||
return 0
|
||||
def dummy(*args, **kwargs):
|
||||
import io
|
||||
return io.UnsupportedOperation(f"{name}: no os specific module found")
|
||||
dummy.__name__ = f"dummy_{name}"
|
||||
return dummy
|
||||
|
||||
def _shim():
|
||||
import _dummy_os, sys
|
||||
sys.modules['os'] = _dummy_os
|
||||
sys.modules['os.path'] = _dummy_os.path
|
||||
sys.modules['os'] = sys.modules['posix'] = sys.modules[__name__]
|
||||
|
||||
import posixpath as path
|
||||
import sys
|
||||
sys.modules['os.path'] = path
|
||||
del sys
|
||||
|
||||
sep = path.sep
|
||||
supports_dir_fd = set()
|
||||
supports_effective_ids = set()
|
||||
supports_fd = set()
|
||||
supports_follow_symlinks = set()
|
||||
|
||||
|
||||
def fspath(path):
|
||||
|
||||
35
Lib/contextlib.py
vendored
35
Lib/contextlib.py
vendored
@@ -145,14 +145,17 @@ class _GeneratorContextManager(
|
||||
except StopIteration:
|
||||
return False
|
||||
else:
|
||||
raise RuntimeError("generator didn't stop")
|
||||
try:
|
||||
raise RuntimeError("generator didn't stop")
|
||||
finally:
|
||||
self.gen.close()
|
||||
else:
|
||||
if value is None:
|
||||
# Need to force instantiation so we can reliably
|
||||
# tell if we get the same exception back
|
||||
value = typ()
|
||||
try:
|
||||
self.gen.throw(typ, value, traceback)
|
||||
self.gen.throw(value)
|
||||
except StopIteration as exc:
|
||||
# Suppress StopIteration *unless* it's the same exception that
|
||||
# was passed to throw(). This prevents a StopIteration
|
||||
@@ -187,7 +190,10 @@ class _GeneratorContextManager(
|
||||
raise
|
||||
exc.__traceback__ = traceback
|
||||
return False
|
||||
raise RuntimeError("generator didn't stop after throw()")
|
||||
try:
|
||||
raise RuntimeError("generator didn't stop after throw()")
|
||||
finally:
|
||||
self.gen.close()
|
||||
|
||||
class _AsyncGeneratorContextManager(
|
||||
_GeneratorContextManagerBase,
|
||||
@@ -212,14 +218,17 @@ class _AsyncGeneratorContextManager(
|
||||
except StopAsyncIteration:
|
||||
return False
|
||||
else:
|
||||
raise RuntimeError("generator didn't stop")
|
||||
try:
|
||||
raise RuntimeError("generator didn't stop")
|
||||
finally:
|
||||
await self.gen.aclose()
|
||||
else:
|
||||
if value is None:
|
||||
# Need to force instantiation so we can reliably
|
||||
# tell if we get the same exception back
|
||||
value = typ()
|
||||
try:
|
||||
await self.gen.athrow(typ, value, traceback)
|
||||
await self.gen.athrow(value)
|
||||
except StopAsyncIteration as exc:
|
||||
# Suppress StopIteration *unless* it's the same exception that
|
||||
# was passed to throw(). This prevents a StopIteration
|
||||
@@ -254,7 +263,10 @@ class _AsyncGeneratorContextManager(
|
||||
raise
|
||||
exc.__traceback__ = traceback
|
||||
return False
|
||||
raise RuntimeError("generator didn't stop after athrow()")
|
||||
try:
|
||||
raise RuntimeError("generator didn't stop after athrow()")
|
||||
finally:
|
||||
await self.gen.aclose()
|
||||
|
||||
|
||||
def contextmanager(func):
|
||||
@@ -441,7 +453,16 @@ class suppress(AbstractContextManager):
|
||||
# exactly reproduce the limitations of the CPython interpreter.
|
||||
#
|
||||
# See http://bugs.python.org/issue12029 for more details
|
||||
return exctype is not None and issubclass(exctype, self._exceptions)
|
||||
if exctype is None:
|
||||
return
|
||||
if issubclass(exctype, self._exceptions):
|
||||
return True
|
||||
if issubclass(exctype, BaseExceptionGroup):
|
||||
match, rest = excinst.split(self._exceptions)
|
||||
if rest is None:
|
||||
return True
|
||||
raise rest
|
||||
return False
|
||||
|
||||
|
||||
class _BaseExitStack:
|
||||
|
||||
12
Lib/io.py
vendored
12
Lib/io.py
vendored
@@ -55,10 +55,15 @@ import _io
|
||||
import abc
|
||||
|
||||
from _io import (DEFAULT_BUFFER_SIZE, BlockingIOError, UnsupportedOperation,
|
||||
open, open_code, FileIO, BytesIO, StringIO, BufferedReader,
|
||||
open, open_code, 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"
|
||||
|
||||
@@ -82,7 +87,10 @@ class BufferedIOBase(_io._BufferedIOBase, IOBase):
|
||||
class TextIOBase(_io._TextIOBase, IOBase):
|
||||
__doc__ = _io._TextIOBase.__doc__
|
||||
|
||||
RawIOBase.register(FileIO)
|
||||
try:
|
||||
RawIOBase.register(FileIO)
|
||||
except NameError:
|
||||
pass
|
||||
|
||||
for klass in (BytesIO, BufferedReader, BufferedWriter, BufferedRandom,
|
||||
BufferedRWPair):
|
||||
|
||||
65
Lib/test/support/testcase.py
vendored
Normal file
65
Lib/test/support/testcase.py
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
from math import copysign, isnan
|
||||
|
||||
|
||||
class ExceptionIsLikeMixin:
|
||||
def assertExceptionIsLike(self, exc, template):
|
||||
"""
|
||||
Passes when the provided `exc` matches the structure of `template`.
|
||||
Individual exceptions don't have to be the same objects or even pass
|
||||
an equality test: they only need to be the same type and contain equal
|
||||
`exc_obj.args`.
|
||||
"""
|
||||
if exc is None and template is None:
|
||||
return
|
||||
|
||||
if template is None:
|
||||
self.fail(f"unexpected exception: {exc}")
|
||||
|
||||
if exc is None:
|
||||
self.fail(f"expected an exception like {template!r}, got None")
|
||||
|
||||
if not isinstance(exc, ExceptionGroup):
|
||||
self.assertEqual(exc.__class__, template.__class__)
|
||||
self.assertEqual(exc.args[0], template.args[0])
|
||||
else:
|
||||
self.assertEqual(exc.message, template.message)
|
||||
self.assertEqual(len(exc.exceptions), len(template.exceptions))
|
||||
for e, t in zip(exc.exceptions, template.exceptions):
|
||||
self.assertExceptionIsLike(e, t)
|
||||
|
||||
|
||||
class FloatsAreIdenticalMixin:
|
||||
def assertFloatsAreIdentical(self, x, y):
|
||||
"""Fail unless floats x and y are identical, in the sense that:
|
||||
(1) both x and y are nans, or
|
||||
(2) both x and y are infinities, with the same sign, or
|
||||
(3) both x and y are zeros, with the same sign, or
|
||||
(4) x and y are both finite and nonzero, and x == y
|
||||
|
||||
"""
|
||||
msg = 'floats {!r} and {!r} are not identical'
|
||||
|
||||
if isnan(x) or isnan(y):
|
||||
if isnan(x) and isnan(y):
|
||||
return
|
||||
elif x == y:
|
||||
if x != 0.0:
|
||||
return
|
||||
# both zero; check that signs match
|
||||
elif copysign(1.0, x) == copysign(1.0, y):
|
||||
return
|
||||
else:
|
||||
msg += ': zeros have different signs'
|
||||
self.fail(msg.format(x, y))
|
||||
|
||||
|
||||
class ComplexesAreIdenticalMixin(FloatsAreIdenticalMixin):
|
||||
def assertComplexesAreIdentical(self, x, y):
|
||||
"""Fail unless complex numbers x and y have equal values and signs.
|
||||
|
||||
In particular, if x and y both have real (or imaginary) part
|
||||
zero, but the zeros have different signs, this test will fail.
|
||||
|
||||
"""
|
||||
self.assertFloatsAreIdentical(x.real, y.real)
|
||||
self.assertFloatsAreIdentical(x.imag, y.imag)
|
||||
114
Lib/test/test_contextlib.py
vendored
114
Lib/test/test_contextlib.py
vendored
@@ -10,6 +10,7 @@ import unittest
|
||||
from contextlib import * # Tests __all__
|
||||
from test import support
|
||||
from test.support import os_helper
|
||||
from test.support.testcase import ExceptionIsLikeMixin
|
||||
import weakref
|
||||
|
||||
|
||||
@@ -158,9 +159,45 @@ class ContextManagerTestCase(unittest.TestCase):
|
||||
yield
|
||||
ctx = whoo()
|
||||
ctx.__enter__()
|
||||
self.assertRaises(
|
||||
RuntimeError, ctx.__exit__, TypeError, TypeError("foo"), None
|
||||
)
|
||||
with self.assertRaises(RuntimeError):
|
||||
ctx.__exit__(TypeError, TypeError("foo"), None)
|
||||
if support.check_impl_detail(cpython=True):
|
||||
# The "gen" attribute is an implementation detail.
|
||||
self.assertFalse(ctx.gen.gi_suspended)
|
||||
|
||||
def test_contextmanager_trap_no_yield(self):
|
||||
@contextmanager
|
||||
def whoo():
|
||||
if False:
|
||||
yield
|
||||
ctx = whoo()
|
||||
with self.assertRaises(RuntimeError):
|
||||
ctx.__enter__()
|
||||
|
||||
def test_contextmanager_trap_second_yield(self):
|
||||
@contextmanager
|
||||
def whoo():
|
||||
yield
|
||||
yield
|
||||
ctx = whoo()
|
||||
ctx.__enter__()
|
||||
with self.assertRaises(RuntimeError):
|
||||
ctx.__exit__(None, None, None)
|
||||
if support.check_impl_detail(cpython=True):
|
||||
# The "gen" attribute is an implementation detail.
|
||||
self.assertFalse(ctx.gen.gi_suspended)
|
||||
|
||||
def test_contextmanager_non_normalised(self):
|
||||
@contextmanager
|
||||
def whoo():
|
||||
try:
|
||||
yield
|
||||
except RuntimeError:
|
||||
raise SyntaxError
|
||||
ctx = whoo()
|
||||
ctx.__enter__()
|
||||
with self.assertRaises(SyntaxError):
|
||||
ctx.__exit__(RuntimeError, None, None)
|
||||
|
||||
def test_contextmanager_except(self):
|
||||
state = []
|
||||
@@ -241,6 +278,23 @@ def woohoo():
|
||||
self.assertEqual(ex.args[0], 'issue29692:Unchained')
|
||||
self.assertIsNone(ex.__cause__)
|
||||
|
||||
def test_contextmanager_wrap_runtimeerror(self):
|
||||
@contextmanager
|
||||
def woohoo():
|
||||
try:
|
||||
yield
|
||||
except Exception as exc:
|
||||
raise RuntimeError(f'caught {exc}') from exc
|
||||
with self.assertRaises(RuntimeError):
|
||||
with woohoo():
|
||||
1 / 0
|
||||
# If the context manager wrapped StopIteration in a RuntimeError,
|
||||
# we also unwrap it, because we can't tell whether the wrapping was
|
||||
# done by the generator machinery or by the generator itself.
|
||||
with self.assertRaises(StopIteration):
|
||||
with woohoo():
|
||||
raise StopIteration
|
||||
|
||||
def _create_contextmanager_attribs(self):
|
||||
def attribs(**kw):
|
||||
def decorate(func):
|
||||
@@ -252,6 +306,7 @@ def woohoo():
|
||||
@attribs(foo='bar')
|
||||
def baz(spam):
|
||||
"""Whee!"""
|
||||
yield
|
||||
return baz
|
||||
|
||||
def test_contextmanager_attribs(self):
|
||||
@@ -308,8 +363,11 @@ def woohoo():
|
||||
|
||||
def test_recursive(self):
|
||||
depth = 0
|
||||
ncols = 0
|
||||
@contextmanager
|
||||
def woohoo():
|
||||
nonlocal ncols
|
||||
ncols += 1
|
||||
nonlocal depth
|
||||
before = depth
|
||||
depth += 1
|
||||
@@ -323,6 +381,7 @@ def woohoo():
|
||||
recursive()
|
||||
|
||||
recursive()
|
||||
self.assertEqual(ncols, 10)
|
||||
self.assertEqual(depth, 0)
|
||||
|
||||
|
||||
@@ -374,12 +433,10 @@ class FileContextTestCase(unittest.TestCase):
|
||||
def testWithOpen(self):
|
||||
tfn = tempfile.mktemp()
|
||||
try:
|
||||
f = None
|
||||
with open(tfn, "w", encoding="utf-8") as f:
|
||||
self.assertFalse(f.closed)
|
||||
f.write("Booh\n")
|
||||
self.assertTrue(f.closed)
|
||||
f = None
|
||||
with self.assertRaises(ZeroDivisionError):
|
||||
with open(tfn, "r", encoding="utf-8") as f:
|
||||
self.assertFalse(f.closed)
|
||||
@@ -1160,7 +1217,7 @@ class TestRedirectStderr(TestRedirectStream, unittest.TestCase):
|
||||
orig_stream = "stderr"
|
||||
|
||||
|
||||
class TestSuppress(unittest.TestCase):
|
||||
class TestSuppress(ExceptionIsLikeMixin, unittest.TestCase):
|
||||
|
||||
@support.requires_docstrings
|
||||
def test_instance_docs(self):
|
||||
@@ -1214,6 +1271,51 @@ class TestSuppress(unittest.TestCase):
|
||||
1/0
|
||||
self.assertTrue(outer_continued)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_exception_groups(self):
|
||||
eg_ve = lambda: ExceptionGroup(
|
||||
"EG with ValueErrors only",
|
||||
[ValueError("ve1"), ValueError("ve2"), ValueError("ve3")],
|
||||
)
|
||||
eg_all = lambda: ExceptionGroup(
|
||||
"EG with many types of exceptions",
|
||||
[ValueError("ve1"), KeyError("ke1"), ValueError("ve2"), KeyError("ke2")],
|
||||
)
|
||||
with suppress(ValueError):
|
||||
raise eg_ve()
|
||||
with suppress(ValueError, KeyError):
|
||||
raise eg_all()
|
||||
with self.assertRaises(ExceptionGroup) as eg1:
|
||||
with suppress(ValueError):
|
||||
raise eg_all()
|
||||
self.assertExceptionIsLike(
|
||||
eg1.exception,
|
||||
ExceptionGroup(
|
||||
"EG with many types of exceptions",
|
||||
[KeyError("ke1"), KeyError("ke2")],
|
||||
),
|
||||
)
|
||||
|
||||
# Check handling of BaseExceptionGroup, using GeneratorExit so that
|
||||
# we don't accidentally discard a ctrl-c with KeyboardInterrupt.
|
||||
with suppress(GeneratorExit):
|
||||
raise BaseExceptionGroup("message", [GeneratorExit()])
|
||||
# If we raise a BaseException group, we can still suppress parts
|
||||
with self.assertRaises(BaseExceptionGroup) as eg1:
|
||||
with suppress(KeyError):
|
||||
raise BaseExceptionGroup("message", [GeneratorExit("g"), KeyError("k")])
|
||||
self.assertExceptionIsLike(
|
||||
eg1.exception, BaseExceptionGroup("message", [GeneratorExit("g")]),
|
||||
)
|
||||
# If we suppress all the leaf BaseExceptions, we get a non-base ExceptionGroup
|
||||
with self.assertRaises(ExceptionGroup) as eg1:
|
||||
with suppress(GeneratorExit):
|
||||
raise BaseExceptionGroup("message", [GeneratorExit("g"), KeyError("k")])
|
||||
self.assertExceptionIsLike(
|
||||
eg1.exception, ExceptionGroup("message", [KeyError("k")]),
|
||||
)
|
||||
|
||||
|
||||
class TestChdir(unittest.TestCase):
|
||||
def make_relative_path(self, *parts):
|
||||
|
||||
102
Lib/test/test_float.py
vendored
102
Lib/test/test_float.py
vendored
@@ -8,7 +8,7 @@ import time
|
||||
import unittest
|
||||
|
||||
from test import support
|
||||
from test.support import import_helper
|
||||
from test.support.testcase import FloatsAreIdenticalMixin
|
||||
from test.test_grammar import (VALID_UNDERSCORE_LITERALS,
|
||||
INVALID_UNDERSCORE_LITERALS)
|
||||
from math import isinf, isnan, copysign, ldexp
|
||||
@@ -19,7 +19,6 @@ try:
|
||||
except ImportError:
|
||||
_testcapi = None
|
||||
|
||||
HAVE_IEEE_754 = float.__getformat__("double").startswith("IEEE")
|
||||
INF = float("inf")
|
||||
NAN = float("nan")
|
||||
|
||||
@@ -742,8 +741,13 @@ class FormatTestCase(unittest.TestCase):
|
||||
|
||||
lhs, rhs = map(str.strip, line.split('->'))
|
||||
fmt, arg = lhs.split()
|
||||
self.assertEqual(fmt % float(arg), rhs)
|
||||
self.assertEqual(fmt % -float(arg), '-' + rhs)
|
||||
f = float(arg)
|
||||
self.assertEqual(fmt % f, rhs)
|
||||
self.assertEqual(fmt % -f, '-' + rhs)
|
||||
if fmt != '%r':
|
||||
fmt2 = fmt[1:]
|
||||
self.assertEqual(format(f, fmt2), rhs)
|
||||
self.assertEqual(format(-f, fmt2), '-' + rhs)
|
||||
|
||||
def test_issue5864(self):
|
||||
self.assertEqual(format(123.456, '.4'), '123.5')
|
||||
@@ -833,7 +837,7 @@ class ReprTestCase(unittest.TestCase):
|
||||
self.assertEqual(repr(float(negs)), str(float(negs)))
|
||||
|
||||
@support.requires_IEEE_754
|
||||
class RoundTestCase(unittest.TestCase):
|
||||
class RoundTestCase(unittest.TestCase, FloatsAreIdenticalMixin):
|
||||
|
||||
def test_inf_nan(self):
|
||||
self.assertRaises(OverflowError, round, INF)
|
||||
@@ -863,10 +867,10 @@ class RoundTestCase(unittest.TestCase):
|
||||
|
||||
def test_small_n(self):
|
||||
for n in [-308, -309, -400, 1-2**31, -2**31, -2**31-1, -2**100]:
|
||||
self.assertEqual(round(123.456, n), 0.0)
|
||||
self.assertEqual(round(-123.456, n), -0.0)
|
||||
self.assertEqual(round(1e300, n), 0.0)
|
||||
self.assertEqual(round(1e-320, n), 0.0)
|
||||
self.assertFloatsAreIdentical(round(123.456, n), 0.0)
|
||||
self.assertFloatsAreIdentical(round(-123.456, n), -0.0)
|
||||
self.assertFloatsAreIdentical(round(1e300, n), 0.0)
|
||||
self.assertFloatsAreIdentical(round(1e-320, n), 0.0)
|
||||
|
||||
def test_overflow(self):
|
||||
self.assertRaises(OverflowError, round, 1.6e308, -308)
|
||||
@@ -1053,32 +1057,22 @@ class InfNanTest(unittest.TestCase):
|
||||
self.assertEqual(copysign(1.0, float('inf')), 1.0)
|
||||
self.assertEqual(copysign(1.0, float('-inf')), -1.0)
|
||||
|
||||
@unittest.skipUnless(getattr(sys, 'float_repr_style', '') == 'short',
|
||||
"applies only when using short float repr style")
|
||||
def test_nan_signs(self):
|
||||
# When using the dtoa.c code, the sign of float('nan') should
|
||||
# be predictable.
|
||||
# The sign of float('nan') should be predictable.
|
||||
self.assertEqual(copysign(1.0, float('nan')), 1.0)
|
||||
self.assertEqual(copysign(1.0, float('-nan')), -1.0)
|
||||
|
||||
|
||||
fromHex = float.fromhex
|
||||
toHex = float.hex
|
||||
class HexFloatTestCase(unittest.TestCase):
|
||||
class HexFloatTestCase(FloatsAreIdenticalMixin, unittest.TestCase):
|
||||
MAX = fromHex('0x.fffffffffffff8p+1024') # max normal
|
||||
MIN = fromHex('0x1p-1022') # min normal
|
||||
TINY = fromHex('0x0.0000000000001p-1022') # min subnormal
|
||||
EPS = fromHex('0x0.0000000000001p0') # diff between 1.0 and next float up
|
||||
|
||||
def identical(self, x, y):
|
||||
# check that floats x and y are identical, or that both
|
||||
# are NaNs
|
||||
if isnan(x) or isnan(y):
|
||||
if isnan(x) == isnan(y):
|
||||
return
|
||||
elif x == y and (x != 0.0 or copysign(1.0, x) == copysign(1.0, y)):
|
||||
return
|
||||
self.fail('%r not identical to %r' % (x, y))
|
||||
self.assertFloatsAreIdentical(x, y)
|
||||
|
||||
def test_ends(self):
|
||||
self.identical(self.MIN, ldexp(1.0, -1022))
|
||||
@@ -1517,69 +1511,5 @@ class HexFloatTestCase(unittest.TestCase):
|
||||
self.assertEqual(getattr(f, 'foo', 'none'), 'bar')
|
||||
|
||||
|
||||
# Test PyFloat_Pack2(), PyFloat_Pack4() and PyFloat_Pack8()
|
||||
# Test PyFloat_Unpack2(), PyFloat_Unpack4() and PyFloat_Unpack8()
|
||||
BIG_ENDIAN = 0
|
||||
LITTLE_ENDIAN = 1
|
||||
EPSILON = {
|
||||
2: 2.0 ** -11, # binary16
|
||||
4: 2.0 ** -24, # binary32
|
||||
8: 2.0 ** -53, # binary64
|
||||
}
|
||||
|
||||
@unittest.skipIf(_testcapi is None, 'needs _testcapi')
|
||||
class PackTests(unittest.TestCase):
|
||||
def test_pack(self):
|
||||
self.assertEqual(_testcapi.float_pack(2, 1.5, BIG_ENDIAN),
|
||||
b'>\x00')
|
||||
self.assertEqual(_testcapi.float_pack(4, 1.5, BIG_ENDIAN),
|
||||
b'?\xc0\x00\x00')
|
||||
self.assertEqual(_testcapi.float_pack(8, 1.5, BIG_ENDIAN),
|
||||
b'?\xf8\x00\x00\x00\x00\x00\x00')
|
||||
self.assertEqual(_testcapi.float_pack(2, 1.5, LITTLE_ENDIAN),
|
||||
b'\x00>')
|
||||
self.assertEqual(_testcapi.float_pack(4, 1.5, LITTLE_ENDIAN),
|
||||
b'\x00\x00\xc0?')
|
||||
self.assertEqual(_testcapi.float_pack(8, 1.5, LITTLE_ENDIAN),
|
||||
b'\x00\x00\x00\x00\x00\x00\xf8?')
|
||||
|
||||
def test_unpack(self):
|
||||
self.assertEqual(_testcapi.float_unpack(b'>\x00', BIG_ENDIAN),
|
||||
1.5)
|
||||
self.assertEqual(_testcapi.float_unpack(b'?\xc0\x00\x00', BIG_ENDIAN),
|
||||
1.5)
|
||||
self.assertEqual(_testcapi.float_unpack(b'?\xf8\x00\x00\x00\x00\x00\x00', BIG_ENDIAN),
|
||||
1.5)
|
||||
self.assertEqual(_testcapi.float_unpack(b'\x00>', LITTLE_ENDIAN),
|
||||
1.5)
|
||||
self.assertEqual(_testcapi.float_unpack(b'\x00\x00\xc0?', LITTLE_ENDIAN),
|
||||
1.5)
|
||||
self.assertEqual(_testcapi.float_unpack(b'\x00\x00\x00\x00\x00\x00\xf8?', LITTLE_ENDIAN),
|
||||
1.5)
|
||||
|
||||
def test_roundtrip(self):
|
||||
large = 2.0 ** 100
|
||||
values = [1.0, 1.5, large, 1.0/7, math.pi]
|
||||
if HAVE_IEEE_754:
|
||||
values.extend((INF, NAN))
|
||||
for value in values:
|
||||
for size in (2, 4, 8,):
|
||||
if size == 2 and value == large:
|
||||
# too large for 16-bit float
|
||||
continue
|
||||
rel_tol = EPSILON[size]
|
||||
for endian in (BIG_ENDIAN, LITTLE_ENDIAN):
|
||||
with self.subTest(value=value, size=size, endian=endian):
|
||||
data = _testcapi.float_pack(size, value, endian)
|
||||
value2 = _testcapi.float_unpack(data, endian)
|
||||
if isnan(value):
|
||||
self.assertTrue(isnan(value2), (value, value2))
|
||||
elif size < 8:
|
||||
self.assertTrue(math.isclose(value2, value, rel_tol=rel_tol),
|
||||
(value, value2))
|
||||
else:
|
||||
self.assertEqual(value2, value)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
1748
Lib/test/test_fstring.py
vendored
1748
Lib/test/test_fstring.py
vendored
File diff suppressed because it is too large
Load Diff
10
README.md
10
README.md
@@ -56,7 +56,7 @@ NOTE: For windows users, please set `RUSTPYTHONPATH` environment variable as `Li
|
||||
You can also install and run RustPython with the following:
|
||||
|
||||
```bash
|
||||
$ cargo install --git https://github.com/RustPython/RustPython
|
||||
$ cargo install --git https://github.com/RustPython/RustPython rustpython
|
||||
$ rustpython
|
||||
Welcome to the magnificent Rust Python interpreter
|
||||
>>>>>
|
||||
@@ -91,13 +91,13 @@ You can compile RustPython to a standalone WebAssembly WASI module so it can run
|
||||
Build
|
||||
|
||||
```bash
|
||||
cargo build --target wasm32-wasi --no-default-features --features freeze-stdlib,stdlib --release
|
||||
cargo build --target wasm32-wasip1 --no-default-features --features freeze-stdlib,stdlib --release
|
||||
```
|
||||
|
||||
Run by wasmer
|
||||
|
||||
```bash
|
||||
wasmer run --dir `pwd` -- target/wasm32-wasi/release/rustpython.wasm `pwd`/extra_tests/snippets/stdlib_random.py
|
||||
wasmer run --dir `pwd` -- target/wasm32-wasip1/release/rustpython.wasm `pwd`/extra_tests/snippets/stdlib_random.py
|
||||
```
|
||||
|
||||
Run by wapm
|
||||
@@ -114,10 +114,10 @@ $ wapm run rustpython
|
||||
You can build the WebAssembly WASI file with:
|
||||
|
||||
```bash
|
||||
cargo build --release --target wasm32-wasi --features="freeze-stdlib"
|
||||
cargo build --release --target wasm32-wasip1 --features="freeze-stdlib"
|
||||
```
|
||||
|
||||
> Note: we use the `freeze-stdlib` to include the standard library inside the binary. You also have to run once `rustup target add wasm32-wasi`.
|
||||
> Note: we use the `freeze-stdlib` to include the standard library inside the binary. You also have to run once `rustup target add wasm32-wasip1`.
|
||||
|
||||
### JIT (Just in time) compiler
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
//! An unresizable vector backed by a `Box<[T]>`
|
||||
|
||||
#![allow(clippy::needless_lifetimes)]
|
||||
|
||||
use std::{
|
||||
alloc,
|
||||
borrow::{Borrow, BorrowMut},
|
||||
cmp, fmt,
|
||||
mem::{self, MaybeUninit},
|
||||
@@ -35,29 +36,11 @@ macro_rules! panic_oob {
|
||||
};
|
||||
}
|
||||
|
||||
fn capacity_overflow() -> ! {
|
||||
panic!("capacity overflow")
|
||||
}
|
||||
|
||||
impl<T> BoxVec<T> {
|
||||
pub fn new(n: usize) -> BoxVec<T> {
|
||||
unsafe {
|
||||
let layout = match alloc::Layout::array::<T>(n) {
|
||||
Ok(l) => l,
|
||||
Err(_) => capacity_overflow(),
|
||||
};
|
||||
let ptr = if mem::size_of::<T>() == 0 {
|
||||
ptr::NonNull::<MaybeUninit<T>>::dangling().as_ptr()
|
||||
} else {
|
||||
let ptr = alloc::alloc(layout);
|
||||
if ptr.is_null() {
|
||||
alloc::handle_alloc_error(layout)
|
||||
}
|
||||
ptr as *mut MaybeUninit<T>
|
||||
};
|
||||
let ptr = ptr::slice_from_raw_parts_mut(ptr, n);
|
||||
let xs = Box::from_raw(ptr);
|
||||
BoxVec { xs, len: 0 }
|
||||
BoxVec {
|
||||
xs: Box::new_uninit_slice(n),
|
||||
len: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,9 +60,9 @@ pub fn bytes_to_int(lit: &[u8], mut base: u32) -> Option<BigInt> {
|
||||
return Some(BigInt::zero());
|
||||
}
|
||||
}
|
||||
16 => lit.get(1).map_or(false, |&b| matches!(b, b'x' | b'X')),
|
||||
2 => lit.get(1).map_or(false, |&b| matches!(b, b'b' | b'B')),
|
||||
8 => lit.get(1).map_or(false, |&b| matches!(b, b'o' | b'O')),
|
||||
16 => lit.get(1).is_some_and(|&b| matches!(b, b'x' | b'X')),
|
||||
2 => lit.get(1).is_some_and(|&b| matches!(b, b'b' | b'B')),
|
||||
8 => lit.get(1).is_some_and(|&b| matches!(b, b'o' | b'O')),
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! A crate to hold types and functions common to all rustpython components.
|
||||
|
||||
#![cfg_attr(target_os = "redox", feature(byte_slice_trim_ascii))]
|
||||
#![cfg_attr(target_os = "redox", feature(byte_slice_trim_ascii, new_uninit))]
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
@@ -2,7 +2,7 @@ use lock_api::{
|
||||
GetThreadId, RawMutex, RawRwLock, RawRwLockDowngrade, RawRwLockRecursive, RawRwLockUpgrade,
|
||||
RawRwLockUpgradeDowngrade,
|
||||
};
|
||||
use std::{cell::Cell, num::NonZeroUsize};
|
||||
use std::{cell::Cell, num::NonZero};
|
||||
|
||||
pub struct RawCellMutex {
|
||||
locked: Cell<bool>,
|
||||
@@ -203,7 +203,7 @@ fn deadlock(lock_kind: &str, ty: &str) -> ! {
|
||||
pub struct SingleThreadId(());
|
||||
unsafe impl GetThreadId for SingleThreadId {
|
||||
const INIT: Self = SingleThreadId(());
|
||||
fn nonzero_thread_id(&self) -> NonZeroUsize {
|
||||
NonZeroUsize::new(1).unwrap()
|
||||
fn nonzero_thread_id(&self) -> NonZero<usize> {
|
||||
NonZero::new(1).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::needless_lifetimes)]
|
||||
|
||||
use lock_api::{MutexGuard, RawMutex};
|
||||
use std::{fmt, marker::PhantomData, ops::Deref};
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::needless_lifetimes)]
|
||||
|
||||
use lock_api::{GetThreadId, GuardNoSend, RawMutex};
|
||||
use std::{
|
||||
cell::UnsafeCell,
|
||||
|
||||
@@ -331,12 +331,13 @@ pub mod levenshtein {
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! ascii {
|
||||
($x:literal) => {{
|
||||
const STR: &str = $x;
|
||||
const _: () = if !STR.is_ascii() {
|
||||
panic!("ascii!() argument is not an ascii string");
|
||||
($x:expr $(,)?) => {{
|
||||
let s = const {
|
||||
let s: &str = $x;
|
||||
assert!(s.is_ascii(), "ascii!() argument is not an ascii string");
|
||||
s
|
||||
};
|
||||
unsafe { $crate::vendored::ascii::AsciiStr::from_ascii_unchecked(STR.as_bytes()) }
|
||||
unsafe { $crate::vendored::ascii::AsciiStr::from_ascii_unchecked(s.as_bytes()) }
|
||||
}};
|
||||
}
|
||||
pub use ascii;
|
||||
|
||||
@@ -3102,19 +3102,18 @@ impl Compiler {
|
||||
Expr::Tuple(ExprTuple { elts, .. }) => elts.iter().any(Self::contains_await),
|
||||
Expr::Set(ExprSet { elts, .. }) => elts.iter().any(Self::contains_await),
|
||||
Expr::Dict(ExprDict { keys, values, .. }) => {
|
||||
keys.iter()
|
||||
.any(|key| key.as_ref().map_or(false, Self::contains_await))
|
||||
keys.iter().flatten().any(Self::contains_await)
|
||||
|| values.iter().any(Self::contains_await)
|
||||
}
|
||||
Expr::Slice(ExprSlice {
|
||||
lower, upper, step, ..
|
||||
}) => {
|
||||
lower.as_ref().map_or(false, |l| Self::contains_await(l))
|
||||
|| upper.as_ref().map_or(false, |u| Self::contains_await(u))
|
||||
|| step.as_ref().map_or(false, |s| Self::contains_await(s))
|
||||
lower.as_deref().is_some_and(Self::contains_await)
|
||||
|| upper.as_deref().is_some_and(Self::contains_await)
|
||||
|| step.as_deref().is_some_and(Self::contains_await)
|
||||
}
|
||||
Expr::Yield(ExprYield { value, .. }) => {
|
||||
value.as_ref().map_or(false, |v| Self::contains_await(v))
|
||||
value.as_deref().is_some_and(Self::contains_await)
|
||||
}
|
||||
Expr::Await(ExprAwait { .. }) => true,
|
||||
Expr::YieldFrom(ExprYieldFrom { value, .. }) => Self::contains_await(value),
|
||||
@@ -3128,9 +3127,7 @@ impl Compiler {
|
||||
..
|
||||
}) => {
|
||||
Self::contains_await(value)
|
||||
|| format_spec
|
||||
.as_ref()
|
||||
.map_or(false, |fs| Self::contains_await(fs))
|
||||
|| format_spec.as_deref().is_some_and(Self::contains_await)
|
||||
}
|
||||
Expr::Name(located_ast::ExprName { .. }) => false,
|
||||
Expr::Lambda(located_ast::ExprLambda { body, .. }) => Self::contains_await(body),
|
||||
|
||||
@@ -102,8 +102,7 @@ impl FrozenLib<Vec<u8>> {
|
||||
/// Encode the given iterator of frozen modules into a compressed vector of bytes
|
||||
pub fn encode<'a, I, B: AsRef<[u8]>>(lib: I) -> FrozenLib<Vec<u8>>
|
||||
where
|
||||
I: IntoIterator<Item = (&'a str, FrozenModule<B>)>,
|
||||
I::IntoIter: ExactSizeIterator + Clone,
|
||||
I: IntoIterator<Item = (&'a str, FrozenModule<B>), IntoIter: ExactSizeIterator + Clone>,
|
||||
{
|
||||
let iter = lib.into_iter();
|
||||
let mut bytes = Vec::new();
|
||||
|
||||
@@ -348,7 +348,7 @@ fn generate_class_def(
|
||||
&& if let Ok(Meta::List(l)) = attr.parse_meta() {
|
||||
l.nested
|
||||
.into_iter()
|
||||
.any(|n| n.get_ident().map_or(false, |p| p == "PyStructSequence"))
|
||||
.any(|n| n.get_ident().is_some_and(|p| p == "PyStructSequence"))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
@@ -558,7 +558,7 @@ impl AttributeExt for Attribute {
|
||||
let has_name = list
|
||||
.nested
|
||||
.iter()
|
||||
.any(|nested_meta| nested_meta.get_path().map_or(false, |p| p.is_ident(name)));
|
||||
.any(|nested_meta| nested_meta.get_path().is_some_and(|p| p.is_ident(name)));
|
||||
if !has_name {
|
||||
list.nested.push(new_item())
|
||||
}
|
||||
|
||||
@@ -32,3 +32,23 @@ assert_raises(ValueError, lambda: 1 << -1)
|
||||
|
||||
# Right shift raises value error on negative
|
||||
assert_raises(ValueError, lambda: 1 >> -1)
|
||||
|
||||
# Bitwise or, and, xor raises value error on incompatible types
|
||||
assert_raises(TypeError, lambda: "abc" | True)
|
||||
assert_raises(TypeError, lambda: "abc" & True)
|
||||
assert_raises(TypeError, lambda: "abc" ^ True)
|
||||
assert_raises(TypeError, lambda: True | "abc")
|
||||
assert_raises(TypeError, lambda: True & "abc")
|
||||
assert_raises(TypeError, lambda: True ^ "abc")
|
||||
assert_raises(TypeError, lambda: "abc" | 1.5)
|
||||
assert_raises(TypeError, lambda: "abc" & 1.5)
|
||||
assert_raises(TypeError, lambda: "abc" ^ 1.5)
|
||||
assert_raises(TypeError, lambda: 1.5 | "abc")
|
||||
assert_raises(TypeError, lambda: 1.5 & "abc")
|
||||
assert_raises(TypeError, lambda: 1.5 ^ "abc")
|
||||
assert_raises(TypeError, lambda: True | 1.5)
|
||||
assert_raises(TypeError, lambda: True & 1.5)
|
||||
assert_raises(TypeError, lambda: True ^ 1.5)
|
||||
assert_raises(TypeError, lambda: 1.5 | True)
|
||||
assert_raises(TypeError, lambda: 1.5 & True)
|
||||
assert_raises(TypeError, lambda: 1.5 ^ True)
|
||||
|
||||
@@ -502,9 +502,9 @@ with TestWithTempDir() as tmpdir:
|
||||
assert set(collected_files) == set(expected_files)
|
||||
|
||||
# system()
|
||||
if "win" not in sys.platform:
|
||||
assert os.system('ls') == 0
|
||||
assert os.system('{') != 0
|
||||
if os.name in ('posix', 'nt'):
|
||||
assert os.system('echo test') == 0
|
||||
assert os.system('&') != 0
|
||||
|
||||
for arg in [None, 1, 1.0, TabError]:
|
||||
assert_raises(TypeError, os.system, arg)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use super::{JitCompileError, JitSig, JitType};
|
||||
use cranelift::codegen::ir::FuncRef;
|
||||
use cranelift::prelude::*;
|
||||
use num_traits::cast::ToPrimitive;
|
||||
use rustpython_compiler_core::bytecode::{
|
||||
@@ -6,8 +8,6 @@ use rustpython_compiler_core::bytecode::{
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::{JitCompileError, JitSig, JitType};
|
||||
|
||||
#[repr(u16)]
|
||||
enum CustomTrapCode {
|
||||
/// Raised when shifting by a negative number
|
||||
@@ -27,6 +27,7 @@ enum JitValue {
|
||||
Bool(Value),
|
||||
None,
|
||||
Tuple(Vec<JitValue>),
|
||||
FuncRef(FuncRef),
|
||||
}
|
||||
|
||||
impl JitValue {
|
||||
@@ -43,14 +44,14 @@ impl JitValue {
|
||||
JitValue::Int(_) => Some(JitType::Int),
|
||||
JitValue::Float(_) => Some(JitType::Float),
|
||||
JitValue::Bool(_) => Some(JitType::Bool),
|
||||
JitValue::None | JitValue::Tuple(_) => None,
|
||||
JitValue::None | JitValue::Tuple(_) | JitValue::FuncRef(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn into_value(self) -> Option<Value> {
|
||||
match self {
|
||||
JitValue::Int(val) | JitValue::Float(val) | JitValue::Bool(val) => Some(val),
|
||||
JitValue::None | JitValue::Tuple(_) => None,
|
||||
JitValue::None | JitValue::Tuple(_) | JitValue::FuncRef(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,6 +69,7 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
|
||||
builder: &'a mut FunctionBuilder<'b>,
|
||||
num_variables: usize,
|
||||
arg_types: &[JitType],
|
||||
ret_type: Option<JitType>,
|
||||
entry_block: Block,
|
||||
) -> FunctionCompiler<'a, 'b> {
|
||||
let mut compiler = FunctionCompiler {
|
||||
@@ -77,7 +79,7 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
|
||||
label_to_block: HashMap::new(),
|
||||
sig: JitSig {
|
||||
args: arg_types.to_vec(),
|
||||
ret: None,
|
||||
ret: ret_type,
|
||||
},
|
||||
};
|
||||
let params = compiler.builder.func.dfg.block_params(entry_block).to_vec();
|
||||
@@ -132,7 +134,7 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
|
||||
}
|
||||
JitValue::Bool(val) => Ok(val),
|
||||
JitValue::None => Ok(self.builder.ins().iconst(types::I8, 0)),
|
||||
JitValue::Tuple(_) => Err(JitCompileError::NotSupported),
|
||||
JitValue::Tuple(_) | JitValue::FuncRef(_) => Err(JitCompileError::NotSupported),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,6 +148,7 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
|
||||
|
||||
pub fn compile<C: bytecode::Constant>(
|
||||
&mut self,
|
||||
func_ref: FuncRef,
|
||||
bytecode: &CodeObject<C>,
|
||||
) -> Result<(), JitCompileError> {
|
||||
// TODO: figure out if this is sufficient -- previously individual labels were associated
|
||||
@@ -177,7 +180,7 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
|
||||
continue;
|
||||
}
|
||||
|
||||
self.add_instruction(instruction, arg, &bytecode.constants)?;
|
||||
self.add_instruction(func_ref, bytecode, instruction, arg)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -229,9 +232,10 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
|
||||
|
||||
pub fn add_instruction<C: bytecode::Constant>(
|
||||
&mut self,
|
||||
func_ref: FuncRef,
|
||||
bytecode: &CodeObject<C>,
|
||||
instruction: Instruction,
|
||||
arg: OpArg,
|
||||
constants: &[C],
|
||||
) -> Result<(), JitCompileError> {
|
||||
match instruction {
|
||||
Instruction::ExtendedArg => Ok(()),
|
||||
@@ -282,7 +286,8 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
|
||||
self.store_variable(idx.get(arg), val)
|
||||
}
|
||||
Instruction::LoadConst { idx } => {
|
||||
let val = self.prepare_const(constants[idx.get(arg) as usize].borrow_constant())?;
|
||||
let val = self
|
||||
.prepare_const(bytecode.constants[idx.get(arg) as usize].borrow_constant())?;
|
||||
self.stack.push(val);
|
||||
Ok(())
|
||||
}
|
||||
@@ -311,7 +316,8 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
|
||||
self.return_value(val)
|
||||
}
|
||||
Instruction::ReturnConst { idx } => {
|
||||
let val = self.prepare_const(constants[idx.get(arg) as usize].borrow_constant())?;
|
||||
let val = self
|
||||
.prepare_const(bytecode.constants[idx.get(arg) as usize].borrow_constant())?;
|
||||
self.return_value(val)
|
||||
}
|
||||
Instruction::CompareOperation { op, .. } => {
|
||||
@@ -508,6 +514,36 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
|
||||
// TODO: block support
|
||||
Ok(())
|
||||
}
|
||||
Instruction::LoadGlobal(idx) => {
|
||||
let name = &bytecode.names[idx.get(arg) as usize];
|
||||
|
||||
if name.as_ref() != bytecode.obj_name.as_ref() {
|
||||
Err(JitCompileError::NotSupported)
|
||||
} else {
|
||||
self.stack.push(JitValue::FuncRef(func_ref));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Instruction::CallFunctionPositional { nargs } => {
|
||||
let nargs = nargs.get(arg);
|
||||
|
||||
let mut args = Vec::new();
|
||||
for _ in 0..nargs {
|
||||
let arg = self.stack.pop().ok_or(JitCompileError::BadBytecode)?;
|
||||
args.push(arg.into_value().unwrap());
|
||||
}
|
||||
|
||||
match self.stack.pop().ok_or(JitCompileError::BadBytecode)? {
|
||||
JitValue::FuncRef(reference) => {
|
||||
let call = self.builder.ins().call(reference, &args);
|
||||
let returns = self.builder.inst_results(call);
|
||||
self.stack.push(JitValue::Int(returns[0]));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(JitCompileError::BadBytecode),
|
||||
}
|
||||
}
|
||||
_ => Err(JitCompileError::NotSupported),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ impl Jit {
|
||||
&mut self,
|
||||
bytecode: &bytecode::CodeObject<C>,
|
||||
args: &[JitType],
|
||||
ret: Option<JitType>,
|
||||
) -> Result<(FuncId, JitSig), JitCompileError> {
|
||||
for arg in args {
|
||||
self.ctx
|
||||
@@ -58,22 +59,13 @@ impl Jit {
|
||||
.push(AbiParam::new(arg.to_cranelift()));
|
||||
}
|
||||
|
||||
let mut builder = FunctionBuilder::new(&mut self.ctx.func, &mut self.builder_context);
|
||||
let entry_block = builder.create_block();
|
||||
builder.append_block_params_for_function_params(entry_block);
|
||||
builder.switch_to_block(entry_block);
|
||||
|
||||
let sig = {
|
||||
let mut compiler =
|
||||
FunctionCompiler::new(&mut builder, bytecode.varnames.len(), args, entry_block);
|
||||
|
||||
compiler.compile(bytecode)?;
|
||||
|
||||
compiler.sig
|
||||
};
|
||||
|
||||
builder.seal_all_blocks();
|
||||
builder.finalize();
|
||||
if ret.is_some() {
|
||||
self.ctx
|
||||
.func
|
||||
.signature
|
||||
.returns
|
||||
.push(AbiParam::new(ret.clone().unwrap().to_cranelift()));
|
||||
}
|
||||
|
||||
let id = self.module.declare_function(
|
||||
&format!("jit_{}", bytecode.obj_name.as_ref()),
|
||||
@@ -81,6 +73,30 @@ impl Jit {
|
||||
&self.ctx.func.signature,
|
||||
)?;
|
||||
|
||||
let func_ref = self.module.declare_func_in_func(id, &mut self.ctx.func);
|
||||
|
||||
let mut builder = FunctionBuilder::new(&mut self.ctx.func, &mut self.builder_context);
|
||||
let entry_block = builder.create_block();
|
||||
builder.append_block_params_for_function_params(entry_block);
|
||||
builder.switch_to_block(entry_block);
|
||||
|
||||
let sig = {
|
||||
let mut compiler = FunctionCompiler::new(
|
||||
&mut builder,
|
||||
bytecode.varnames.len(),
|
||||
args,
|
||||
ret,
|
||||
entry_block,
|
||||
);
|
||||
|
||||
compiler.compile(func_ref, bytecode)?;
|
||||
|
||||
compiler.sig
|
||||
};
|
||||
|
||||
builder.seal_all_blocks();
|
||||
builder.finalize();
|
||||
|
||||
self.module.define_function(id, &mut self.ctx)?;
|
||||
|
||||
self.module.clear_context(&mut self.ctx);
|
||||
@@ -92,10 +108,11 @@ impl Jit {
|
||||
pub fn compile<C: bytecode::Constant>(
|
||||
bytecode: &bytecode::CodeObject<C>,
|
||||
args: &[JitType],
|
||||
ret: Option<JitType>,
|
||||
) -> Result<CompiledCode, JitCompileError> {
|
||||
let mut jit = Jit::new();
|
||||
|
||||
let (id, sig) = jit.build_function(bytecode, args)?;
|
||||
let (id, sig) = jit.build_function(bytecode, args, ret)?;
|
||||
|
||||
jit.module.finalize_definitions();
|
||||
|
||||
|
||||
@@ -27,7 +27,17 @@ impl Function {
|
||||
arg_types.push(arg_type);
|
||||
}
|
||||
|
||||
rustpython_jit::compile(&self.code, &arg_types).expect("Compile failure")
|
||||
let ret_type = match self.annotations.get("return") {
|
||||
Some(StackValue::String(annotation)) => match annotation.as_str() {
|
||||
"int" => Some(JitType::Int),
|
||||
"float" => Some(JitType::Float),
|
||||
"bool" => Some(JitType::Bool),
|
||||
_ => panic!("Unrecognised jit type"),
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
|
||||
rustpython_jit::compile(&self.code, &arg_types, ret_type).expect("Compile failure")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -113,3 +113,15 @@ fn test_unpack_tuple() {
|
||||
assert_eq!(unpack_tuple(0, 1), Ok(1));
|
||||
assert_eq!(unpack_tuple(1, 2), Ok(2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_recursive_fib() {
|
||||
let fib = jit_function! { fib(n: i64) -> i64 => r##"
|
||||
def fib(n: int) -> int:
|
||||
if n == 0 or n == 1:
|
||||
return 1
|
||||
return fib(n-1) + fib(n-2)
|
||||
"## };
|
||||
|
||||
assert_eq!(fib(10), Ok(89));
|
||||
}
|
||||
|
||||
@@ -7,6 +7,6 @@ FEATURES_FOR_WAPM=(stdlib zlib)
|
||||
|
||||
export BUILDTIME_RUSTPYTHONPATH="/lib/rustpython"
|
||||
|
||||
cargo build --release --target wasm32-wasi --no-default-features --features="${FEATURES_FOR_WAPM[*]}"
|
||||
cargo build --release --target wasm32-wasip1 --no-default-features --features="${FEATURES_FOR_WAPM[*]}"
|
||||
|
||||
wapm publish
|
||||
|
||||
@@ -58,6 +58,7 @@ use std::process::ExitCode;
|
||||
pub use interpreter::InterpreterConfig;
|
||||
pub use rustpython_vm as vm;
|
||||
pub use settings::{opts_with_clap, InstallPipMode, RunMode};
|
||||
pub use shell::run_shell;
|
||||
|
||||
/// The main cli of the `rustpython` interpreter. This function will return `std::process::ExitCode`
|
||||
/// based on the return code of the python code ran through the cli.
|
||||
|
||||
@@ -22,7 +22,7 @@ fn split_idents_on_dot(line: &str) -> Option<(usize, Vec<String>)> {
|
||||
match c {
|
||||
'.' => {
|
||||
// check for a double dot
|
||||
if i != 0 && words.last().map_or(false, |s| s.is_empty()) {
|
||||
if i != 0 && words.last().is_some_and(|s| s.is_empty()) {
|
||||
return None;
|
||||
}
|
||||
reverse_string(words.last_mut().unwrap());
|
||||
|
||||
@@ -1238,7 +1238,7 @@ mod array {
|
||||
|
||||
let res = match array_a.cmp(&array_b) {
|
||||
// fast path for same ArrayContentType type
|
||||
Ok(partial_ord) => partial_ord.map_or(false, |ord| op.eval_ord(ord)),
|
||||
Ok(partial_ord) => partial_ord.is_some_and(|ord| op.eval_ord(ord)),
|
||||
Err(()) => {
|
||||
let iter = Iterator::zip(array_a.iter(vm), array_b.iter(vm));
|
||||
|
||||
|
||||
@@ -129,18 +129,16 @@ mod _contextvars {
|
||||
|
||||
super::CONTEXTS.with(|ctxs| {
|
||||
let mut ctxs = ctxs.borrow_mut();
|
||||
if !ctxs
|
||||
.last()
|
||||
.map_or(false, |ctx| ctx.get_id() == zelf.get_id())
|
||||
{
|
||||
// TODO: use Vec::pop_if once stabilized
|
||||
if ctxs.last().is_some_and(|ctx| ctx.get_id() == zelf.get_id()) {
|
||||
let _ = ctxs.pop();
|
||||
Ok(())
|
||||
} else {
|
||||
let msg =
|
||||
"cannot exit context: thread state references a different context object"
|
||||
.to_owned();
|
||||
return Err(vm.new_runtime_error(msg));
|
||||
Err(vm.new_runtime_error(msg))
|
||||
}
|
||||
|
||||
let _ = ctxs.pop();
|
||||
Ok(())
|
||||
})?;
|
||||
zelf.inner.entered.set(false);
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ mod grp {
|
||||
#[pyfunction]
|
||||
fn getgrall(vm: &VirtualMachine) -> PyResult<Vec<PyObjectRef>> {
|
||||
// setgrent, getgrent, etc are not thread safe. Could use fgetgrent_r, but this is easier
|
||||
static GETGRALL: parking_lot::Mutex<()> = parking_lot::const_mutex(());
|
||||
static GETGRALL: parking_lot::Mutex<()> = parking_lot::Mutex::new(());
|
||||
let _guard = GETGRALL.lock();
|
||||
let mut list = Vec::new();
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// to allow `mod foo {}` in foo.rs; clippy thinks this is a mistake/misunderstanding of
|
||||
// how `mod` works, but we want this sometimes for pymodule declarations
|
||||
#![allow(clippy::module_inception)]
|
||||
#![cfg_attr(target_os = "redox", feature(raw_ref_op))]
|
||||
|
||||
#[macro_use]
|
||||
extern crate rustpython_derive;
|
||||
|
||||
@@ -36,7 +36,7 @@ mod platform {
|
||||
// based off winsock2.h: https://gist.github.com/piscisaureus/906386#file-winsock2-h-L128-L141
|
||||
|
||||
pub unsafe fn FD_SET(fd: RawFd, set: *mut fd_set) {
|
||||
let mut slot = std::ptr::addr_of_mut!((*set).fd_array).cast::<RawFd>();
|
||||
let mut slot = (&raw mut (*set).fd_array).cast::<RawFd>();
|
||||
let fd_count = (*set).fd_count;
|
||||
for _ in 0..fd_count {
|
||||
if *slot == fd {
|
||||
|
||||
@@ -2217,7 +2217,7 @@ mod _socket {
|
||||
fn as_slice(&self) -> &[netioapi::MIB_IF_ROW2] {
|
||||
unsafe {
|
||||
let p = self.ptr.as_ptr();
|
||||
let ptr = ptr::addr_of!((*p).Table) as *const netioapi::MIB_IF_ROW2;
|
||||
let ptr = &raw const (*p).Table as *const netioapi::MIB_IF_ROW2;
|
||||
std::slice::from_raw_parts(ptr, (*p).NumEntries as usize)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ mod _sqlite {
|
||||
ffi::{c_int, c_longlong, c_uint, c_void, CStr},
|
||||
fmt::Debug,
|
||||
ops::Deref,
|
||||
ptr::{addr_of_mut, null, null_mut},
|
||||
ptr::{null, null_mut},
|
||||
thread::ThreadId,
|
||||
};
|
||||
|
||||
@@ -1180,14 +1180,10 @@ mod _sqlite {
|
||||
)
|
||||
};
|
||||
|
||||
// TODO: replace with Result.inspect_err when stable
|
||||
if let Err(exc) = db.check(ret, vm) {
|
||||
db.check(ret, vm).inspect_err(|_| {
|
||||
// create_collation do not call destructor if error occur
|
||||
let _ = unsafe { Box::from_raw(data) };
|
||||
Err(exc)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
@@ -2398,7 +2394,7 @@ mod _sqlite {
|
||||
let ret = unsafe {
|
||||
sqlite3_open_v2(
|
||||
path,
|
||||
addr_of_mut!(db),
|
||||
&raw mut db,
|
||||
SQLITE_OPEN_READWRITE
|
||||
| SQLITE_OPEN_CREATE
|
||||
| if uri { SQLITE_OPEN_URI } else { 0 },
|
||||
|
||||
@@ -85,7 +85,7 @@ mod unicodedata {
|
||||
}
|
||||
|
||||
fn check_age(&self, c: char) -> bool {
|
||||
Age::of(c).map_or(false, |age| age.actual() <= self.unic_version)
|
||||
Age::of(c).is_some_and(|age| age.actual() <= self.unic_version)
|
||||
}
|
||||
|
||||
fn extract_char(&self, character: PyStrRef, vm: &VirtualMachine) -> PyResult<Option<char>> {
|
||||
|
||||
@@ -73,12 +73,12 @@ thiserror = { workspace = true }
|
||||
thread_local = { workspace = true }
|
||||
memchr = { workspace = true }
|
||||
|
||||
caseless = "0.2.1"
|
||||
caseless = "0.2.2"
|
||||
flamer = { version = "0.4", optional = true }
|
||||
half = "2"
|
||||
memoffset = "0.9.1"
|
||||
optional = "0.5.0"
|
||||
result-like = "0.4.6"
|
||||
result-like = "0.5.0"
|
||||
timsort = "0.1.2"
|
||||
|
||||
## unicode stuff
|
||||
|
||||
@@ -385,17 +385,14 @@ pub trait AnyStr {
|
||||
let (end_len, i_diff) = match *ch {
|
||||
b'\n' => (keep, 1),
|
||||
b'\r' => {
|
||||
let is_rn = enumerated.peek().map_or(false, |(_, ch)| **ch == b'\n');
|
||||
let is_rn = enumerated.next_if(|(_, ch)| **ch == b'\n').is_some();
|
||||
if is_rn {
|
||||
let _ = enumerated.next();
|
||||
(keep + keep, 2)
|
||||
} else {
|
||||
(keep, 1)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
continue;
|
||||
}
|
||||
_ => continue,
|
||||
};
|
||||
let range = last_i..i + end_len;
|
||||
last_i = i + i_diff;
|
||||
|
||||
@@ -390,7 +390,7 @@ impl PyAsyncGenAThrow {
|
||||
}
|
||||
|
||||
fn ignored_close(&self, res: &PyResult<PyIterReturn>) -> bool {
|
||||
res.as_ref().map_or(false, |v| match v {
|
||||
res.as_ref().is_ok_and(|v| match v {
|
||||
PyIterReturn::Return(obj) => obj.payload_is::<PyAsyncGenWrappedValue>(),
|
||||
PyIterReturn::StopIteration(_) => false,
|
||||
})
|
||||
|
||||
@@ -127,8 +127,10 @@ impl PyBool {
|
||||
let lhs = get_value(&lhs);
|
||||
let rhs = get_value(&rhs);
|
||||
(lhs || rhs).to_pyobject(vm)
|
||||
} else if let Some(lhs) = lhs.payload::<PyInt>() {
|
||||
lhs.or(rhs, vm).to_pyobject(vm)
|
||||
} else {
|
||||
get_py_int(&lhs).or(rhs, vm).to_pyobject(vm)
|
||||
vm.ctx.not_implemented()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,8 +143,10 @@ impl PyBool {
|
||||
let lhs = get_value(&lhs);
|
||||
let rhs = get_value(&rhs);
|
||||
(lhs && rhs).to_pyobject(vm)
|
||||
} else if let Some(lhs) = lhs.payload::<PyInt>() {
|
||||
lhs.and(rhs, vm).to_pyobject(vm)
|
||||
} else {
|
||||
get_py_int(&lhs).and(rhs, vm).to_pyobject(vm)
|
||||
vm.ctx.not_implemented()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,8 +159,10 @@ impl PyBool {
|
||||
let lhs = get_value(&lhs);
|
||||
let rhs = get_value(&rhs);
|
||||
(lhs ^ rhs).to_pyobject(vm)
|
||||
} else if let Some(lhs) = lhs.payload::<PyInt>() {
|
||||
lhs.xor(rhs, vm).to_pyobject(vm)
|
||||
} else {
|
||||
get_py_int(&lhs).xor(rhs, vm).to_pyobject(vm)
|
||||
vm.ctx.not_implemented()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,7 +213,3 @@ pub(crate) fn init(context: &Context) {
|
||||
pub(crate) fn get_value(obj: &PyObject) -> bool {
|
||||
!obj.payload::<PyInt>().unwrap().as_bigint().is_zero()
|
||||
}
|
||||
|
||||
fn get_py_int(obj: &PyObject) -> &PyInt {
|
||||
obj.payload::<PyInt>().unwrap()
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ impl GetDescriptor for PyMethodDescriptor {
|
||||
let bound = match obj {
|
||||
Some(obj) => {
|
||||
if descr.method.flags.contains(PyMethodFlags::METHOD) {
|
||||
if cls.map_or(false, |c| c.fast_isinstance(vm.ctx.types.type_type)) {
|
||||
if cls.is_some_and(|c| c.fast_isinstance(vm.ctx.types.type_type)) {
|
||||
obj
|
||||
} else {
|
||||
return Err(vm.new_type_error(format!(
|
||||
|
||||
@@ -728,11 +728,8 @@ impl ExactSizeIterator for DictIter<'_> {
|
||||
}
|
||||
|
||||
#[pyclass]
|
||||
trait DictView: PyPayload + PyClassDef + Iterable + Representable
|
||||
where
|
||||
Self::ReverseIter: PyPayload,
|
||||
{
|
||||
type ReverseIter;
|
||||
trait DictView: PyPayload + PyClassDef + Iterable + Representable {
|
||||
type ReverseIter: PyPayload;
|
||||
|
||||
fn dict(&self) -> &PyDictRef;
|
||||
fn item(vm: &VirtualMachine, key: PyObjectRef, value: PyObjectRef) -> PyObjectRef;
|
||||
|
||||
@@ -506,7 +506,8 @@ impl PyFunction {
|
||||
zelf.jitted_code
|
||||
.get_or_try_init(|| {
|
||||
let arg_types = jitfunc::get_jit_arg_types(&zelf, vm)?;
|
||||
rustpython_jit::compile(&zelf.code.code, &arg_types)
|
||||
let ret_type = jitfunc::jit_ret_type(&zelf, vm)?;
|
||||
rustpython_jit::compile(&zelf.code.code, &arg_types, ret_type)
|
||||
.map_err(|err| jitfunc::new_jit_error(err.to_string(), vm))
|
||||
})
|
||||
.map(drop)
|
||||
|
||||
@@ -52,7 +52,7 @@ fn get_jit_arg_type(dict: &PyDictRef, name: &str, vm: &VirtualMachine) -> PyResu
|
||||
Ok(JitType::Bool)
|
||||
} else {
|
||||
Err(new_jit_error(
|
||||
"Jit requires argument to be either int or float".to_owned(),
|
||||
"Jit requires argument to be either int, float or bool".to_owned(),
|
||||
vm,
|
||||
))
|
||||
}
|
||||
@@ -106,6 +106,25 @@ pub fn get_jit_arg_types(func: &Py<PyFunction>, vm: &VirtualMachine) -> PyResult
|
||||
}
|
||||
}
|
||||
|
||||
pub fn jit_ret_type(func: &Py<PyFunction>, vm: &VirtualMachine) -> PyResult<Option<JitType>> {
|
||||
let func_obj: PyObjectRef = func.as_ref().to_owned();
|
||||
let annotations = func_obj.get_attr("__annotations__", vm)?;
|
||||
if vm.is_none(&annotations) {
|
||||
Err(new_jit_error(
|
||||
"Jitting function requires return type to have annotations".to_owned(),
|
||||
vm,
|
||||
))
|
||||
} else if let Ok(dict) = PyDictRef::try_from_object(vm, annotations) {
|
||||
if dict.contains_key("return", vm) {
|
||||
get_jit_arg_type(&dict, "return", vm).map_or(Ok(None), |t| Ok(Some(t)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
} else {
|
||||
Err(vm.new_type_error("Function annotations aren't a dict".to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
fn get_jit_value(vm: &VirtualMachine, obj: &PyObject) -> Result<AbiValue, ArgsError> {
|
||||
// This does exact type checks as subclasses of int/float can't be passed to jitted functions
|
||||
let cls = obj.class();
|
||||
|
||||
@@ -126,7 +126,7 @@ impl PyMappingProxy {
|
||||
match &self.mapping {
|
||||
MappingProxyInner::Class(class) => Ok(key
|
||||
.as_interned_str(vm)
|
||||
.map_or(false, |key| class.attributes.read().contains_key(key))),
|
||||
.is_some_and(|key| class.attributes.read().contains_key(key))),
|
||||
MappingProxyInner::Mapping(mapping) => mapping.to_sequence().contains(key, vm),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -903,9 +903,8 @@ impl PyStr {
|
||||
let end_len = match ch {
|
||||
'\n' => 1,
|
||||
'\r' => {
|
||||
let is_rn = enumerated.peek().map_or(false, |(_, ch)| *ch == '\n');
|
||||
let is_rn = enumerated.next_if(|(_, ch)| *ch == '\n').is_some();
|
||||
if is_rn {
|
||||
let _ = enumerated.next();
|
||||
2
|
||||
} else {
|
||||
1
|
||||
@@ -913,9 +912,7 @@ impl PyStr {
|
||||
}
|
||||
'\x0b' | '\x0c' | '\x1c' | '\x1d' | '\x1e' | '\u{0085}' | '\u{2028}'
|
||||
| '\u{2029}' => ch.len_utf8(),
|
||||
_ => {
|
||||
continue;
|
||||
}
|
||||
_ => continue,
|
||||
};
|
||||
let range = if args.keepends {
|
||||
last_i..i + end_len
|
||||
@@ -1160,7 +1157,7 @@ impl PyStr {
|
||||
#[pymethod]
|
||||
fn isidentifier(&self) -> bool {
|
||||
let mut chars = self.as_str().chars();
|
||||
let is_identifier_start = chars.next().map_or(false, |c| c == '_' || is_xid_start(c));
|
||||
let is_identifier_start = chars.next().is_some_and(|c| c == '_' || is_xid_start(c));
|
||||
// a string is not an identifier if it has whitespace or starts with a number
|
||||
is_identifier_start && chars.all(is_xid_continue)
|
||||
}
|
||||
|
||||
@@ -296,7 +296,7 @@ pub(crate) fn cformat_bytes(
|
||||
return if is_mapping
|
||||
|| values_obj
|
||||
.payload::<tuple::PyTuple>()
|
||||
.map_or(false, |e| e.is_empty())
|
||||
.is_some_and(|e| e.is_empty())
|
||||
{
|
||||
for (_, part) in format.iter_mut() {
|
||||
match part {
|
||||
@@ -397,7 +397,7 @@ pub(crate) fn cformat_string(
|
||||
return if is_mapping
|
||||
|| values_obj
|
||||
.payload::<tuple::PyTuple>()
|
||||
.map_or(false, |e| e.is_empty())
|
||||
.is_some_and(|e| e.is_empty())
|
||||
{
|
||||
for (_, part) in format.iter() {
|
||||
match part {
|
||||
|
||||
@@ -55,19 +55,14 @@ const fn zst_ref_out_of_thin_air<T: 'static>(x: T) -> &'static T {
|
||||
// would never get called anyway if we consider this semantically a Box::leak(Box::new(x))-type
|
||||
// operation. if T isn't zero-sized, we don't have to worry about it because we'll fail to compile.
|
||||
std::mem::forget(x);
|
||||
trait Zst: Sized + 'static {
|
||||
const THIN_AIR: &'static Self = {
|
||||
if std::mem::size_of::<Self>() == 0 {
|
||||
// SAFETY: we just confirmed that Self is zero-sized, so we can
|
||||
// pull a value of it out of thin air.
|
||||
unsafe { std::ptr::NonNull::<Self>::dangling().as_ref() }
|
||||
} else {
|
||||
panic!("can't use a non-zero-sized type here")
|
||||
}
|
||||
};
|
||||
const {
|
||||
if std::mem::size_of::<T>() != 0 {
|
||||
panic!("can't use a non-zero-sized type here")
|
||||
}
|
||||
// SAFETY: we just confirmed that T is zero-sized, so we can
|
||||
// pull a value of it out of thin air.
|
||||
unsafe { std::ptr::NonNull::<T>::dangling().as_ref() }
|
||||
}
|
||||
impl<T: 'static> Zst for T {}
|
||||
<T as Zst>::THIN_AIR
|
||||
}
|
||||
|
||||
/// Get the [`STATIC_FUNC`](IntoPyNativeFn::STATIC_FUNC) of the passed function. The same
|
||||
|
||||
@@ -228,12 +228,8 @@ mod sealed {
|
||||
}
|
||||
|
||||
/// A sealed marker trait for `DictKey` types that always become an exact instance of `str`
|
||||
pub trait InternableString
|
||||
where
|
||||
Self: sealed::SealedInternable + ToPyObject + AsRef<Self::Interned>,
|
||||
Self::Interned: MaybeInternedString,
|
||||
{
|
||||
type Interned: ?Sized;
|
||||
pub trait InternableString: sealed::SealedInternable + ToPyObject + AsRef<Self::Interned> {
|
||||
type Interned: MaybeInternedString + ?Sized;
|
||||
fn into_pyref_exact(self, str_type: PyTypeRef) -> PyRefExact<PyStr>;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#![allow(clippy::upper_case_acronyms)]
|
||||
#![doc(html_logo_url = "https://raw.githubusercontent.com/RustPython/RustPython/main/logo.png")]
|
||||
#![doc(html_root_url = "https://docs.rs/rustpython-vm/")]
|
||||
#![cfg_attr(target_os = "redox", feature(raw_ref_op))]
|
||||
|
||||
#[cfg(feature = "flame-it")]
|
||||
#[macro_use]
|
||||
|
||||
@@ -322,7 +322,7 @@ unsafe impl Link for WeakLink {
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn pointers(target: NonNull<Self::Target>) -> NonNull<Pointers<Self::Target>> {
|
||||
NonNull::new_unchecked(ptr::addr_of_mut!((*target.as_ptr()).0.payload.pointers))
|
||||
NonNull::new_unchecked(&raw mut (*target.as_ptr()).0.payload.pointers)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -364,8 +364,11 @@ impl PyWeak {
|
||||
fn drop_inner(&self) {
|
||||
let dealloc = {
|
||||
let mut guard = unsafe { self.parent.as_ref().lock() };
|
||||
let offset = memoffset::offset_of!(PyInner<PyWeak>, payload);
|
||||
let pyinner = (self as *const Self as usize - offset) as *const PyInner<Self>;
|
||||
let offset = std::mem::offset_of!(PyInner<PyWeak>, payload);
|
||||
let pyinner = (self as *const Self)
|
||||
.cast::<u8>()
|
||||
.wrapping_sub(offset)
|
||||
.cast::<PyInner<Self>>();
|
||||
let node_ptr = unsafe { NonNull::new_unchecked(pyinner as *mut Py<Self>) };
|
||||
// the list doesn't have ownership over its PyRef<PyWeak>! we're being dropped
|
||||
// right now so that should be obvious!!
|
||||
@@ -1044,7 +1047,7 @@ impl<T: PyObjectPayload> PyRef<T> {
|
||||
pub fn leak(pyref: Self) -> &'static Py<T> {
|
||||
let ptr = pyref.ptr;
|
||||
std::mem::forget(pyref);
|
||||
unsafe { &*ptr.as_ptr() }
|
||||
unsafe { ptr.as_ref() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{fmt, marker::PhantomData};
|
||||
use std::fmt;
|
||||
|
||||
use crate::{
|
||||
object::{
|
||||
@@ -16,25 +16,18 @@ pub(in crate::object) struct PyObjVTable {
|
||||
}
|
||||
|
||||
impl PyObjVTable {
|
||||
pub fn of<T: PyObjectPayload>() -> &'static Self {
|
||||
struct Helper<T: PyObjectPayload>(PhantomData<T>);
|
||||
trait VtableHelper {
|
||||
const VTABLE: PyObjVTable;
|
||||
pub const fn of<T: PyObjectPayload>() -> &'static Self {
|
||||
&PyObjVTable {
|
||||
drop_dealloc: drop_dealloc_obj::<T>,
|
||||
debug: debug_obj::<T>,
|
||||
trace: const {
|
||||
if T::IS_TRACE {
|
||||
Some(try_trace_obj::<T>)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
}
|
||||
impl<T: PyObjectPayload> VtableHelper for Helper<T> {
|
||||
const VTABLE: PyObjVTable = PyObjVTable {
|
||||
drop_dealloc: drop_dealloc_obj::<T>,
|
||||
debug: debug_obj::<T>,
|
||||
trace: {
|
||||
if T::IS_TRACE {
|
||||
Some(try_trace_obj::<T>)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
&Helper::<T>::VTABLE
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ impl PyMapping<'_> {
|
||||
// PyMapping::Check
|
||||
#[inline]
|
||||
pub fn check(obj: &PyObject) -> bool {
|
||||
Self::find_methods(obj).map_or(false, |x| x.as_ref().check())
|
||||
Self::find_methods(obj).is_some_and(|x| x.as_ref().check())
|
||||
}
|
||||
|
||||
pub fn find_methods(obj: &PyObject) -> Option<PointerSlot<PyMappingMethods>> {
|
||||
|
||||
@@ -429,8 +429,7 @@ mod builtins {
|
||||
let fd_matches = |obj, expected| {
|
||||
vm.call_method(obj, "fileno", ())
|
||||
.and_then(|o| i64::try_from_object(vm, o))
|
||||
.ok()
|
||||
.map_or(false, |fd| fd == expected)
|
||||
.is_ok_and(|fd| fd == expected)
|
||||
};
|
||||
|
||||
// everything is normalish, we can just rely on rustyline to use stdin/stdout
|
||||
|
||||
@@ -684,7 +684,7 @@ mod decl {
|
||||
self.grouper
|
||||
.as_ref()
|
||||
.and_then(|g| g.upgrade())
|
||||
.map_or(false, |ref current_grouper| grouper.is(current_grouper))
|
||||
.is_some_and(|current_grouper| grouper.is(¤t_grouper))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ pub(crate) mod module {
|
||||
.as_path()
|
||||
.parent()
|
||||
.and_then(|dst_parent| dst_parent.join(&args.src).symlink_metadata().ok())
|
||||
.map_or(false, |meta| meta.is_dir());
|
||||
.is_some_and(|meta| meta.is_dir());
|
||||
let res = if dir {
|
||||
win_fs::symlink_dir(args.src.path, args.dst.path)
|
||||
} else {
|
||||
|
||||
@@ -142,6 +142,7 @@ pub(super) mod _os {
|
||||
protocol::PyIterReturn,
|
||||
recursion::ReprGuard,
|
||||
types::{IterNext, Iterable, PyStructSequence, Representable, SelfIter},
|
||||
utils::ToCString,
|
||||
vm::VirtualMachine,
|
||||
AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
|
||||
};
|
||||
@@ -273,8 +274,8 @@ pub(super) mod _os {
|
||||
fn remove(path: OsPath, dir_fd: DirFd<0>, vm: &VirtualMachine) -> PyResult<()> {
|
||||
let [] = dir_fd.0;
|
||||
let is_junction = cfg!(windows)
|
||||
&& fs::metadata(&path).map_or(false, |meta| meta.file_type().is_dir())
|
||||
&& fs::symlink_metadata(&path).map_or(false, |meta| meta.file_type().is_symlink());
|
||||
&& fs::metadata(&path).is_ok_and(|meta| meta.file_type().is_dir())
|
||||
&& fs::symlink_metadata(&path).is_ok_and(|meta| meta.file_type().is_symlink());
|
||||
let res = if is_junction {
|
||||
fs::remove_dir(&path)
|
||||
} else {
|
||||
@@ -1028,6 +1029,14 @@ pub(super) mod _os {
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(any(unix, windows))]
|
||||
#[pyfunction]
|
||||
fn system(command: PyStrRef, vm: &VirtualMachine) -> PyResult<i32> {
|
||||
let cstr = command.to_cstring(vm)?;
|
||||
let x = unsafe { libc::system(cstr.as_ptr()) };
|
||||
Ok(x)
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
struct UtimeArgs {
|
||||
path: OsPath,
|
||||
|
||||
@@ -901,13 +901,6 @@ pub mod module {
|
||||
nix::unistd::pipe2(oflags).map_err(|err| err.into_pyexception(vm))
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn system(command: PyStrRef, vm: &VirtualMachine) -> PyResult<i32> {
|
||||
let cstr = command.to_cstring(vm)?;
|
||||
let x = unsafe { libc::system(cstr.as_ptr()) };
|
||||
Ok(x)
|
||||
}
|
||||
|
||||
fn _chmod(
|
||||
path: OsPath,
|
||||
dir_fd: DirFd<0>,
|
||||
|
||||
@@ -92,7 +92,7 @@ mod pwd {
|
||||
#[pyfunction]
|
||||
fn getpwall(vm: &VirtualMachine) -> PyResult<Vec<PyObjectRef>> {
|
||||
// setpwent, getpwent, etc are not thread safe. Could use fgetpwent_r, but this is easier
|
||||
static GETPWALL: parking_lot::Mutex<()> = parking_lot::const_mutex(());
|
||||
static GETPWALL: parking_lot::Mutex<()> = parking_lot::Mutex::new(());
|
||||
let _guard = GETPWALL.lock();
|
||||
let mut list = Vec::new();
|
||||
|
||||
|
||||
@@ -11,10 +11,12 @@ pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
|
||||
|
||||
#[pymodule]
|
||||
pub(crate) mod _signal {
|
||||
#[cfg(any(unix, windows))]
|
||||
use crate::{
|
||||
convert::{IntoPyException, TryFromBorrowedObject},
|
||||
signal, Py, PyObjectRef, PyResult, VirtualMachine,
|
||||
Py,
|
||||
};
|
||||
use crate::{signal, PyObjectRef, PyResult, VirtualMachine};
|
||||
use std::sync::atomic::{self, Ordering};
|
||||
|
||||
#[cfg(any(unix, windows))]
|
||||
@@ -110,28 +112,28 @@ pub(crate) mod _signal {
|
||||
module: &Py<crate::builtins::PyModule>,
|
||||
vm: &VirtualMachine,
|
||||
) {
|
||||
let sig_dfl = vm.new_pyobj(SIG_DFL as u8);
|
||||
let sig_ign = vm.new_pyobj(SIG_IGN as u8);
|
||||
|
||||
for signum in 1..NSIG {
|
||||
let handler = unsafe { libc::signal(signum as i32, SIG_IGN) };
|
||||
if handler != SIG_ERR {
|
||||
unsafe { libc::signal(signum as i32, handler) };
|
||||
}
|
||||
let py_handler = if handler == SIG_DFL {
|
||||
Some(sig_dfl.clone())
|
||||
} else if handler == SIG_IGN {
|
||||
Some(sig_ign.clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
vm.signal_handlers.as_deref().unwrap().borrow_mut()[signum] = py_handler;
|
||||
}
|
||||
|
||||
let int_handler = module
|
||||
.get_attr("default_int_handler", vm)
|
||||
.expect("_signal does not have this attr?");
|
||||
if vm.state.settings.install_signal_handlers {
|
||||
let sig_dfl = vm.new_pyobj(SIG_DFL as u8);
|
||||
let sig_ign = vm.new_pyobj(SIG_IGN as u8);
|
||||
|
||||
for signum in 1..NSIG {
|
||||
let handler = unsafe { libc::signal(signum as i32, SIG_IGN) };
|
||||
if handler != SIG_ERR {
|
||||
unsafe { libc::signal(signum as i32, handler) };
|
||||
}
|
||||
let py_handler = if handler == SIG_DFL {
|
||||
Some(sig_dfl.clone())
|
||||
} else if handler == SIG_IGN {
|
||||
Some(sig_ign.clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
vm.signal_handlers.as_deref().unwrap().borrow_mut()[signum] = py_handler;
|
||||
}
|
||||
|
||||
let int_handler = module
|
||||
.get_attr("default_int_handler", vm)
|
||||
.expect("_signal does not have this attr?");
|
||||
signal(libc::SIGINT, int_handler, vm).expect("Failed to set sigint handler");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,7 +470,7 @@ impl PyType {
|
||||
.attributes
|
||||
.read()
|
||||
.get(identifier!(ctx, __hash__))
|
||||
.map_or(false, |a| a.is(&ctx.none));
|
||||
.is_some_and(|a| a.is(&ctx.none));
|
||||
let wrapper = if is_unhashable {
|
||||
hash_not_implemented
|
||||
} else {
|
||||
@@ -945,7 +945,7 @@ pub trait GetDescriptor: PyPayload {
|
||||
|
||||
#[inline]
|
||||
fn _cls_is(cls: &Option<PyObjectRef>, other: &impl Borrow<PyObject>) -> bool {
|
||||
cls.as_ref().map_or(false, |cls| other.borrow().is(cls))
|
||||
cls.as_ref().is_some_and(|cls| other.borrow().is(cls))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -133,13 +133,10 @@ impl VirtualMachine {
|
||||
let import_func = ctx.none();
|
||||
let profile_func = RefCell::new(ctx.none());
|
||||
let trace_func = RefCell::new(ctx.none());
|
||||
// hack to get around const array repeat expressions, rust issue #79270
|
||||
const NONE: Option<PyObjectRef> = None;
|
||||
// putting it in a const optimizes better, prevents linear initialization of the array
|
||||
#[allow(clippy::declare_interior_mutable_const)]
|
||||
const SIGNAL_HANDLERS: RefCell<[Option<PyObjectRef>; signal::NSIG]> =
|
||||
RefCell::new([NONE; signal::NSIG]);
|
||||
let signal_handlers = Some(Box::new(SIGNAL_HANDLERS));
|
||||
let signal_handlers = Some(Box::new(
|
||||
// putting it in a const optimizes better, prevents linear initialization of the array
|
||||
const { RefCell::new([const { None }; signal::NSIG]) },
|
||||
));
|
||||
|
||||
let module_inits = stdlib::get_module_inits();
|
||||
|
||||
|
||||
@@ -298,8 +298,8 @@ impl VirtualMachine {
|
||||
}
|
||||
|
||||
if let Some(slot_c) = class_c.slots.as_number.left_ternary_op(op_slot) {
|
||||
if slot_a.map_or(false, |slot_a| (slot_a as usize) != (slot_c as usize))
|
||||
&& slot_b.map_or(false, |slot_b| (slot_b as usize) != (slot_c as usize))
|
||||
if slot_a.is_some_and(|slot_a| slot_a != slot_c)
|
||||
&& slot_b.is_some_and(|slot_b| slot_b != slot_c)
|
||||
{
|
||||
let ret = slot_c(a, b, c, self)?;
|
||||
if !ret.is(&self.ctx.not_implemented) {
|
||||
|
||||
@@ -367,7 +367,7 @@ fn setup_context(
|
||||
|
||||
// Stack level comparisons to Python code is off by one as there is no
|
||||
// warnings-related stack level to avoid.
|
||||
if stack_level <= 0 || f.as_ref().map_or(false, |frame| frame.is_internal_frame()) {
|
||||
if stack_level <= 0 || f.as_ref().is_some_and(|frame| frame.is_internal_frame()) {
|
||||
loop {
|
||||
stack_level -= 1;
|
||||
if stack_level <= 0 {
|
||||
|
||||
@@ -8,7 +8,7 @@ repository = "https://github.com/RustPython/RustPython"
|
||||
|
||||
[[module]]
|
||||
name = "rustpython"
|
||||
source = "target/wasm32-wasi/release/rustpython.wasm"
|
||||
source = "target/wasm32-wasip1/release/rustpython.wasm"
|
||||
abi = "wasi"
|
||||
|
||||
[[command]]
|
||||
|
||||
@@ -4,24 +4,25 @@
|
||||
"description": "Bindings to the RustPython library for WebAssembly",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"codemirror": "^5.42.0",
|
||||
"local-echo": "^0.2.0",
|
||||
"xterm": "^3.8.0"
|
||||
"@codemirror/lang-python": "^6.1.6",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/xterm": "^5.3.0",
|
||||
"codemirror": "^6.0.1",
|
||||
"upgrade": "^1.1.0",
|
||||
"xterm-readline": "^1.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@wasm-tool/wasm-pack-plugin": "^1.1.0",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"css-loader": "^3.4.1",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"mini-css-extract-plugin": "^0.9.0",
|
||||
"raw-loader": "^4.0.0",
|
||||
"serve": "^11.0.2",
|
||||
"webpack": "^4.16.3",
|
||||
"webpack-cli": "^3.1.0",
|
||||
"webpack-dev-server": "^3.1.5"
|
||||
"@wasm-tool/wasm-pack-plugin": "^1.7.0",
|
||||
"css-loader": "^7.1.2",
|
||||
"html-webpack-plugin": "^5.6.3",
|
||||
"mini-css-extract-plugin": "^2.9.2",
|
||||
"serve": "^14.2.4",
|
||||
"webpack": "^5.97.1",
|
||||
"webpack-cli": "^6.0.1",
|
||||
"webpack-dev-server": "^5.2.0"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "webpack-dev-server -d",
|
||||
"dev": "webpack serve",
|
||||
"build": "webpack",
|
||||
"dist": "webpack --mode production",
|
||||
"test": "webpack --mode production && cd ../tests && pytest"
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import asyncweb
|
||||
import whlimport
|
||||
|
||||
whlimport.setup()
|
||||
|
||||
# make sys.modules['os'] a dumb version of the os module, which has posixpath
|
||||
# available as os.path as well as a few other utilities, but will raise an
|
||||
# OSError for anything that actually requires an OS
|
||||
import _dummy_os
|
||||
_dummy_os._shim()
|
||||
|
||||
import asyncweb
|
||||
import whlimport
|
||||
|
||||
whlimport.setup()
|
||||
|
||||
@asyncweb.main
|
||||
async def main():
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
browser's devtools and play with <code>rp.pyEval('1 + 1')</code>
|
||||
</p>
|
||||
<div id="code-wrapper">
|
||||
<textarea id="code"><%= defaultSnippet %></textarea>
|
||||
<select id="snippets">
|
||||
<% for (const name of snippets) { %>
|
||||
<option
|
||||
@@ -77,7 +76,7 @@
|
||||
<a href="https://github.com/RustPython/RustPython">
|
||||
<img
|
||||
style="position: absolute; top: 0; right: 0; border: 0;"
|
||||
src="https://s3.amazonaws.com/github/ribbons/forkme_right_green_007200.png"
|
||||
src="https://github.blog/wp-content/uploads/2008/12/forkme_right_green_007200.png"
|
||||
alt="Fork me on GitHub"
|
||||
/>
|
||||
</a>
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import './style.css';
|
||||
import 'xterm/lib/xterm.css';
|
||||
import CodeMirror from 'codemirror';
|
||||
import 'codemirror/mode/python/python';
|
||||
import 'codemirror/addon/comment/comment';
|
||||
import 'codemirror/lib/codemirror.css';
|
||||
import { Terminal } from 'xterm';
|
||||
import LocalEchoController from 'local-echo';
|
||||
import '@xterm/xterm/css/xterm.css';
|
||||
import { EditorView, basicSetup } from 'codemirror';
|
||||
import { keymap } from '@codemirror/view';
|
||||
import { indentUnit } from '@codemirror/language';
|
||||
import { indentWithTab } from '@codemirror/commands';
|
||||
import { python } from '@codemirror/lang-python';
|
||||
import { Terminal } from '@xterm/xterm';
|
||||
import { FitAddon } from '@xterm/addon-fit';
|
||||
import { Readline } from 'xterm-readline';
|
||||
|
||||
let rp;
|
||||
|
||||
@@ -22,23 +24,24 @@ import('rustpython')
|
||||
document.getElementById('error').textContent = e;
|
||||
});
|
||||
|
||||
const editor = CodeMirror.fromTextArea(document.getElementById('code'), {
|
||||
extraKeys: {
|
||||
'Ctrl-Enter': runCodeFromTextarea,
|
||||
'Cmd-Enter': runCodeFromTextarea,
|
||||
'Shift-Tab': 'indentLess',
|
||||
'Ctrl-/': 'toggleComment',
|
||||
'Cmd-/': 'toggleComment',
|
||||
Tab: (editor) => {
|
||||
var spaces = Array(editor.getOption('indentUnit') + 1).join(' ');
|
||||
editor.replaceSelection(spaces);
|
||||
},
|
||||
},
|
||||
lineNumbers: true,
|
||||
mode: 'text/x-python',
|
||||
indentUnit: 4,
|
||||
autofocus: true,
|
||||
const fixedHeightEditor = EditorView.theme({
|
||||
'&': { height: '100%' },
|
||||
'.cm-scroller': { overflow: 'auto' },
|
||||
});
|
||||
const editor = new EditorView({
|
||||
parent: document.getElementById('code-wrapper'),
|
||||
extensions: [
|
||||
basicSetup,
|
||||
python(),
|
||||
keymap.of(
|
||||
{ key: 'Ctrl-Enter', mac: 'Cmd-Enter', run: runCodeFromTextarea },
|
||||
indentWithTab,
|
||||
),
|
||||
indentUnit.of(' '),
|
||||
fixedHeightEditor,
|
||||
],
|
||||
});
|
||||
editor.focus();
|
||||
|
||||
const consoleElement = document.getElementById('console');
|
||||
const errorElement = document.getElementById('error');
|
||||
@@ -48,7 +51,7 @@ function runCodeFromTextarea() {
|
||||
consoleElement.value = '';
|
||||
errorElement.textContent = '';
|
||||
|
||||
const code = editor.getValue();
|
||||
const code = editor.state.doc.toString();
|
||||
try {
|
||||
rp.pyExec(code, {
|
||||
stdout: (output) => {
|
||||
@@ -78,18 +81,25 @@ function updateSnippet() {
|
||||
// the require here creates a webpack context; it's fine to use it
|
||||
// dynamically.
|
||||
// https://webpack.js.org/guides/dependency-management/
|
||||
const { default: snippet } = require(
|
||||
`raw-loader!../snippets/${selected}.py`,
|
||||
);
|
||||
const snippet = require(`../snippets/${selected}.py?raw`);
|
||||
|
||||
editor.setValue(snippet);
|
||||
runCodeFromTextarea();
|
||||
editor.dispatch({
|
||||
changes: { from: 0, to: editor.state.doc.length, insert: snippet },
|
||||
});
|
||||
}
|
||||
function updateSnippetAndRun() {
|
||||
updateSnippet();
|
||||
requestAnimationFrame(runCodeFromTextarea);
|
||||
}
|
||||
updateSnippet();
|
||||
|
||||
const term = new Terminal();
|
||||
const readline = new Readline();
|
||||
const fitAddon = new FitAddon();
|
||||
term.loadAddon(readline);
|
||||
term.loadAddon(fitAddon);
|
||||
term.open(document.getElementById('terminal'));
|
||||
|
||||
const localEcho = new LocalEchoController(term);
|
||||
fitAddon.fit();
|
||||
|
||||
let terminalVM;
|
||||
|
||||
@@ -107,40 +117,33 @@ finally:
|
||||
}
|
||||
|
||||
async function readPrompts() {
|
||||
let continuing = false;
|
||||
let continuing = '';
|
||||
|
||||
while (true) {
|
||||
const ps1 = getPrompt('ps1');
|
||||
const ps2 = getPrompt('ps2');
|
||||
let input;
|
||||
let input = await readline.read(getPrompt(continuing ? 'ps2' : 'ps1'));
|
||||
if (input.endsWith('\n')) input = input.slice(0, -1);
|
||||
if (continuing) {
|
||||
const prom = localEcho.read(ps2, ps2);
|
||||
localEcho._activePrompt.prompt = ps1;
|
||||
localEcho._input = localEcho.history.entries.pop() + '\n';
|
||||
localEcho._cursor = localEcho._input.length;
|
||||
localEcho._active = true;
|
||||
input = await prom;
|
||||
if (!input.endsWith('\n')) continue;
|
||||
} else {
|
||||
input = await localEcho.read(ps1, ps2);
|
||||
input = continuing += '\n' + input;
|
||||
if (!continuing.endsWith('\n')) continue;
|
||||
}
|
||||
try {
|
||||
console.log([input]);
|
||||
terminalVM.execSingle(input);
|
||||
} catch (err) {
|
||||
if (err.canContinue) {
|
||||
continuing = true;
|
||||
continuing = input;
|
||||
continue;
|
||||
} else if (err instanceof WebAssembly.RuntimeError) {
|
||||
err = window.__RUSTPYTHON_ERROR || err;
|
||||
}
|
||||
localEcho.println(err);
|
||||
readline.print('' + err);
|
||||
}
|
||||
continuing = false;
|
||||
continuing = '';
|
||||
}
|
||||
}
|
||||
|
||||
function onReady() {
|
||||
snippets.addEventListener('change', updateSnippet);
|
||||
snippets.addEventListener('change', updateSnippetAndRun);
|
||||
document
|
||||
.getElementById('run-btn')
|
||||
.addEventListener('click', runCodeFromTextarea);
|
||||
@@ -148,7 +151,7 @@ function onReady() {
|
||||
runCodeFromTextarea();
|
||||
|
||||
terminalVM = rp.vmStore.init('term_vm');
|
||||
terminalVM.setStdout((data) => localEcho.print(data));
|
||||
terminalVM.setStdout((data) => readline.print(data));
|
||||
readPrompts().catch((err) => console.error(err));
|
||||
|
||||
// so that the test knows that we're ready
|
||||
|
||||
@@ -3,7 +3,7 @@ textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
#code,
|
||||
#code-wrapper,
|
||||
#console {
|
||||
height: 30vh;
|
||||
width: calc(100% - 3px);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const WasmPackPlugin = require('@wasm-tool/wasm-pack-plugin');
|
||||
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
@@ -12,6 +11,7 @@ module.exports = (env = {}) => {
|
||||
output: {
|
||||
path: path.join(__dirname, 'dist'),
|
||||
filename: 'index.js',
|
||||
clean: true,
|
||||
},
|
||||
mode: 'development',
|
||||
resolve: {
|
||||
@@ -28,10 +28,17 @@ module.exports = (env = {}) => {
|
||||
test: /\.css$/,
|
||||
use: [MiniCssExtractPlugin.loader, 'css-loader'],
|
||||
},
|
||||
{
|
||||
resourceQuery: '?raw',
|
||||
type: 'asset/source',
|
||||
},
|
||||
{
|
||||
test: /\.wasm$/,
|
||||
type: 'webassembly/async',
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new CleanWebpackPlugin(),
|
||||
new HtmlWebpackPlugin({
|
||||
filename: 'index.html',
|
||||
template: 'src/index.ejs',
|
||||
@@ -51,6 +58,9 @@ module.exports = (env = {}) => {
|
||||
filename: 'styles.css',
|
||||
}),
|
||||
],
|
||||
experiments: {
|
||||
asyncWebAssembly: true,
|
||||
},
|
||||
};
|
||||
if (!env.noWasmPack) {
|
||||
config.plugins.push(
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use crate::js_module;
|
||||
use crate::vm_class::{stored_vm_from_wasm, WASMVirtualMachine};
|
||||
use js_sys::{Array, ArrayBuffer, Object, Promise, Reflect, SyntaxError, Uint8Array};
|
||||
use rustpython_parser::ParseErrorType;
|
||||
use rustpython_parser::{lexer::LexicalErrorType, ParseErrorType};
|
||||
use rustpython_vm::{
|
||||
builtins::PyBaseExceptionRef,
|
||||
compiler::{CompileError, CompileErrorType},
|
||||
@@ -34,7 +34,7 @@ extern "C" {
|
||||
|
||||
pub fn py_err_to_js_err(vm: &VirtualMachine, py_err: &PyBaseExceptionRef) -> JsValue {
|
||||
let jserr = vm.try_class("_js", "JSError").ok();
|
||||
let js_arg = if jserr.map_or(false, |jserr| py_err.fast_isinstance(&jserr)) {
|
||||
let js_arg = if jserr.is_some_and(|jserr| py_err.fast_isinstance(&jserr)) {
|
||||
py_err.get_arg(0)
|
||||
} else {
|
||||
None
|
||||
@@ -258,7 +258,15 @@ pub fn syntax_err(err: CompileError) -> SyntaxError {
|
||||
&"col".into(),
|
||||
&(err.location.unwrap().column.get()).into(),
|
||||
);
|
||||
let can_continue = matches!(&err.error, CompileErrorType::Parse(ParseErrorType::Eof));
|
||||
let can_continue = matches!(
|
||||
&err.error,
|
||||
CompileErrorType::Parse(
|
||||
ParseErrorType::Eof
|
||||
| ParseErrorType::Lexical(LexicalErrorType::Eof)
|
||||
| ParseErrorType::Lexical(LexicalErrorType::IndentationError)
|
||||
| ParseErrorType::UnrecognizedToken(rustpython_parser::Tok::Dedent, _)
|
||||
)
|
||||
);
|
||||
let _ = Reflect::set(&js_err, &"canContinue".into(), &can_continue.into());
|
||||
js_err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user