Compare commits

..

10 Commits

Author SHA1 Message Date
Ashwin Naren
c8fd3bd683 Build wasm on release 2025-01-03 14:56:58 +09:00
Shubham Patil
1abaf87abe Temporarily disable release notes generation
Release creation fails with this error:
```
HTTP 422: Validation Failed (https://api.github.com/repos/RustPython/RustPython/releases)
body is too long (maximum is 125000 characters)
Error: Process completed with exit code 1.
```

Most likely because there's no previous release to diff of from.

Disabling, getting a green release & enabling back might fix this.
2024-12-30 16:44:55 +09:00
Shubham Patil
38593fbd85 Check operand types in bool or, and, xor to be PyInt (#5461)
* Added Tests for Bitwise or, and, xor type error

* Sync binary operator order comment with actual implementation

* Check operand types in bool or, and, xor to be PyInt

PyNumber methods are expected to type check both arguments.

Dispatch is not done by inverting parameter order for __r<op>__ (example __ror__) when calls are handled via PyNumberMethods
2024-12-30 16:44:27 +09:00
Ankit Goel
8d187fd275 Bump result-like to 0.5.0 2024-12-28 11:38:00 +09:00
Shubham Patil
646cc81656 Add GitHub Binary Release Pipeline for RustPython (#5456) 2024-12-28 11:34:53 +09:00
carsonzhu
01f7536b36 expose run_shell 2024-12-11 17:33:36 +09:00
Jeong, YunWon
97e5ec02f8 Merge pull request #5449 from key262yek/update_test_float_from_CPython_v3.12.7
Update test float from c python v3.12.7
2024-12-06 12:50:55 +09:00
Oskar Skog
3dced01af0 Move os.system from posix.rs to os.rs
Fixes #5100
2024-12-06 12:19:34 +09:00
0cf4534c5c copy new file "Lib/test/support/testcase.py" for test_float.py
Some checks failed
CI / Run rust tests (macos-latest) (pull_request) Has been cancelled
CI / Run rust tests (windows-latest) (pull_request) Has been cancelled
CI / Ensure compilation on various targets (pull_request) Has been cancelled
CI / Run snippets and cpython tests (macos-latest) (pull_request) Has been cancelled
CI / Run snippets and cpython tests (ubuntu-latest) (pull_request) Has been cancelled
CI / Run snippets and cpython tests (windows-latest) (pull_request) Has been cancelled
CI / Check Rust code with rustfmt and clippy (pull_request) Has been cancelled
CI / Run tests under miri (pull_request) Has been cancelled
CI / Check the WASM package and demo (pull_request) Has been cancelled
CI / Run snippets and cpython tests on wasm-wasi (pull_request) Has been cancelled
CI / Run rust tests (ubuntu-latest) (pull_request) Has been cancelled
2024-12-05 15:29:35 +09:00
044f66fba3 copy from cpython v3.12.7 2024-12-05 15:29:35 +09:00
12 changed files with 850 additions and 1266 deletions

127
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,127 @@
on:
push:
branches: [main]
name: Release
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-wasi
- name: Build RustPython
run: cargo build --target wasm32-wasi --no-default-features --features freeze-stdlib,stdlib --release
- name: Rename Binary
run: cp target/wasm32-wasi/release/rustpython.wasm target/rustpython-release-wasm32-wasi.wasm
- name: Upload Binary Artifacts
uses: actions/upload-artifact@v4
with:
name: rustpython-release-wasm32-wasi
path: target/rustpython-release-wasm32-wasi.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: |
today=$(date '+%Y-%m-%d')
gh release create "$today-$tag-$run" \
--repo="$GITHUB_REPOSITORY" \
--title="RustPython Release $today-$tag #$run" \
--target="$tag" \
bin/rustpython-release-*

17
Cargo.lock generated
View File

@@ -1566,13 +1566,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 +1824,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]]

65
Lib/test/support/testcase.py vendored Normal file
View 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)

102
Lib/test/test_float.py vendored
View File

@@ -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()

1744
Lib/test/test_fstring.py vendored

File diff suppressed because it is too large Load Diff

View File

@@ -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)

View File

@@ -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)

View File

@@ -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.

View File

@@ -78,7 +78,7 @@ 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

View File

@@ -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()
}

View File

@@ -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,
};
@@ -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,

View File

@@ -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>,