Skip to content

Commit 18a27ea

Browse files
authored
Merge pull request RustPython#2417 from RustPython/update-python-version
Declare ourselves as Python 3.9
2 parents bd9ab67 + 59794cd commit 18a27ea

File tree

13 files changed

+134
-36
lines changed

13 files changed

+134
-36
lines changed

Lib/site.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -265,13 +265,14 @@ def joinuser(*args):
265265
def _get_path(userbase):
266266
version = sys.version_info
267267

268+
# XXX RUSTPYTHON: we replace pythonx.y with rustpythonx.y
268269
if os.name == 'nt':
269-
return f'{userbase}\\Python{version[0]}{version[1]}\\site-packages'
270+
return f'{userbase}\\RustPython{version[0]}{version[1]}\\site-packages'
270271

271272
if sys.platform == 'darwin' and sys._framework:
272-
return f'{userbase}/lib/python/site-packages'
273+
return f'{userbase}/lib/rustpython/site-packages'
273274

274-
return f'{userbase}/lib/python{version[0]}.{version[1]}/site-packages'
275+
return f'{userbase}/lib/rustpython{version[0]}.{version[1]}/site-packages'
275276

276277

277278
def getuserbase():

Lib/sysconfig.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@
8080
},
8181
}
8282

83+
# XXX RUSTPYTHON: replace python with rustpython in all these paths
84+
for group in _INSTALL_SCHEMES.values():
85+
for key in group.keys():
86+
group[key] = group[key].replace("Python", "RustPython").replace("python", "rustpython")
87+
88+
8389
_SCHEME_KEYS = ('stdlib', 'platstdlib', 'purelib', 'platlib', 'include',
8490
'scripts', 'data')
8591

Lib/test/test_builtin.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import types
1818
import unittest
1919
import warnings
20+
from functools import partial
2021
from contextlib import ExitStack
2122
from operator import neg
2223
from test.support import (
@@ -1143,11 +1144,24 @@ def test_pow(self):
11431144
self.assertAlmostEqual(pow(-1, 0.5), 1j)
11441145
self.assertAlmostEqual(pow(-1, 1/3), 0.5 + 0.8660254037844386j)
11451146

1146-
self.assertRaises(ValueError, pow, -1, -2, 3)
1147+
# See test_pow for additional tests for three-argument pow.
1148+
self.assertEqual(pow(-1, -2, 3), 1)
11471149
self.assertRaises(ValueError, pow, 1, 2, 0)
11481150

11491151
self.assertRaises(TypeError, pow)
11501152

1153+
# Test passing in arguments as keywords.
1154+
self.assertEqual(pow(0, exp=0), 1)
1155+
self.assertEqual(pow(base=2, exp=4), 16)
1156+
self.assertEqual(pow(base=5, exp=2, mod=14), 11)
1157+
twopow = partial(pow, base=2)
1158+
self.assertEqual(twopow(exp=5), 32)
1159+
fifth_power = partial(pow, exp=5)
1160+
self.assertEqual(fifth_power(2), 32)
1161+
mod10 = partial(pow, mod=10)
1162+
self.assertEqual(mod10(2, 6), 4)
1163+
self.assertEqual(mod10(exp=6, base=2), 4)
1164+
11511165
def test_input(self):
11521166
self.write_testfile()
11531167
fp = open(TESTFN, 'r')

Lib/test/test_pow.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import math
12
import unittest
23

34
class PowTest(unittest.TestCase):
@@ -119,5 +120,30 @@ def test_bug705231(self):
119120
eq(pow(a, -fiveto), expected)
120121
eq(expected, 1.0) # else we didn't push fiveto to evenness
121122

123+
def test_negative_exponent(self):
124+
for a in range(-50, 50):
125+
for m in range(-50, 50):
126+
with self.subTest(a=a, m=m):
127+
if m != 0 and math.gcd(a, m) == 1:
128+
# Exponent -1 should give an inverse, with the
129+
# same sign as m.
130+
inv = pow(a, -1, m)
131+
self.assertEqual(inv, inv % m)
132+
self.assertEqual((inv * a - 1) % m, 0)
133+
134+
# Larger exponents
135+
self.assertEqual(pow(a, -2, m), pow(inv, 2, m))
136+
self.assertEqual(pow(a, -3, m), pow(inv, 3, m))
137+
self.assertEqual(pow(a, -1001, m), pow(inv, 1001, m))
138+
139+
else:
140+
with self.assertRaises(ValueError):
141+
pow(a, -1, m)
142+
with self.assertRaises(ValueError):
143+
pow(a, -2, m)
144+
with self.assertRaises(ValueError):
145+
pow(a, -1001, m)
146+
147+
122148
if __name__ == "__main__":
123149
unittest.main()

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
# [RustPython](https://rustpython.github.io/)
44

5-
A Python-3 (CPython >= 3.5.0) Interpreter written in Rust :snake: :scream:
5+
A Python-3 (CPython >= 3.8.0) Interpreter written in Rust :snake: :scream:
66
:metal:.
77

88
[![Build Status](https://github.com/RustPython/RustPython/workflows/CI/badge.svg)](https://github.com/RustPython/RustPython/actions?query=workflow%3ACI)

derive/src/from_args.rs

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ impl ParameterKind {
2828
}
2929

3030
struct ArgAttribute {
31+
name: Option<String>,
3132
kind: ParameterKind,
3233
default: Option<DefaultValue>,
3334
}
@@ -60,6 +61,7 @@ impl ArgAttribute {
6061
})?;
6162

6263
let mut attribute = ArgAttribute {
64+
name: None,
6365
kind,
6466
default: None,
6567
};
@@ -99,6 +101,15 @@ impl ArgAttribute {
99101
Lit::Str(ref val) => self.default = Some(Some(val.parse()?)),
100102
_ => bail_span!(name_value, "Expected string value for default argument"),
101103
}
104+
} else if path_eq(&name_value.path, "name") {
105+
if self.name.is_some() {
106+
bail_span!(name_value, "already have a name")
107+
}
108+
109+
match &name_value.lit {
110+
Lit::Str(val) => self.name = Some(val.value()),
111+
_ => bail_span!(name_value, "Expected string value for name argument"),
112+
}
102113
} else {
103114
bail_span!(name_value, "Unrecognised pyarg attribute");
104115
}
@@ -118,6 +129,7 @@ fn generate_field(field: &Field) -> Result<TokenStream2, Diagnostic> {
118129
.collect::<Result<Vec<_>, _>>()?;
119130
let attr = if pyarg_attrs.is_empty() {
120131
ArgAttribute {
132+
name: None,
121133
kind: ParameterKind::PositionalOrKeyword,
122134
default: None,
123135
}
@@ -127,19 +139,19 @@ fn generate_field(field: &Field) -> Result<TokenStream2, Diagnostic> {
127139
bail_span!(field, "Multiple pyarg attributes on field");
128140
};
129141

130-
let name = &field.ident;
131-
if let Some(name) = name {
132-
if name.to_string().starts_with("_phantom") {
133-
return Ok(quote! {
134-
#name: ::std::marker::PhantomData,
135-
});
136-
}
142+
let name = field.ident.as_ref().unwrap();
143+
let namestring = name.to_string();
144+
if namestring.starts_with("_phantom") {
145+
return Ok(quote! {
146+
#name: ::std::marker::PhantomData,
147+
});
137148
}
138149
if let ParameterKind::Flatten = attr.kind {
139150
return Ok(quote! {
140151
#name: ::rustpython_vm::function::FromArgs::from_args(vm, args)?,
141152
});
142153
}
154+
let pyname = attr.name.unwrap_or(namestring);
143155
let middle = quote! {
144156
.map(|x| ::rustpython_vm::pyobject::TryFromObject::try_from_object(vm, x)).transpose()?
145157
};
@@ -155,7 +167,7 @@ fn generate_field(field: &Field) -> Result<TokenStream2, Diagnostic> {
155167
::rustpython_vm::function::ArgumentError::TooFewArgs
156168
},
157169
ParameterKind::KeywordOnly => quote! {
158-
::rustpython_vm::function::ArgumentError::RequiredKeywordArgument(stringify!(#name))
170+
::rustpython_vm::function::ArgumentError::RequiredKeywordArgument(#pyname)
159171
},
160172
ParameterKind::Flatten => unreachable!(),
161173
};
@@ -172,12 +184,12 @@ fn generate_field(field: &Field) -> Result<TokenStream2, Diagnostic> {
172184
}
173185
ParameterKind::PositionalOrKeyword => {
174186
quote! {
175-
#name: args.take_positional_keyword(stringify!(#name))#middle#ending,
187+
#name: args.take_positional_keyword(#pyname)#middle#ending,
176188
}
177189
}
178190
ParameterKind::KeywordOnly => {
179191
quote! {
180-
#name: args.take_keyword(stringify!(#name))#middle#ending,
192+
#name: args.take_keyword(#pyname)#middle#ending,
181193
}
182194
}
183195
ParameterKind::Flatten => unreachable!(),

extra_tests/snippets/math_basics.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,7 @@
4949
assert_raises(TypeError, pow, 2, 4, 5.0)
5050
assert_raises(TypeError, pow, 2, 4.0, 5)
5151
assert_raises(TypeError, pow, 2.0, 4, 5)
52-
from sys import version_info
53-
if version_info < (3, 8):
54-
assert_raises(ValueError, pow, 2, -1, 5)
55-
else: # https://docs.python.org/3/whatsnew/3.8.html#other-language-changes
56-
assert pow(2, -1, 5) == 3
52+
assert pow(2, -1, 5) == 3
5753
assert_raises(ValueError, pow, 2, 2, 0)
5854

5955
# bitwise

vm/src/builtins/int.rs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -445,11 +445,37 @@ impl PyInt {
445445
self.general_op(
446446
other,
447447
|a, b| {
448-
if b.is_negative() {
449-
Err(vm.new_value_error("modular inverses not supported".to_owned()))
448+
let i = if b.is_negative() {
449+
// modular multiplicative inverse
450+
// based on rust-num/num-integer#10, should hopefully be published soon
451+
fn normalize(a: BigInt, n: &BigInt) -> BigInt {
452+
let a = a % n;
453+
if a.is_negative() {
454+
a + n
455+
} else {
456+
a
457+
}
458+
}
459+
fn inverse(a: BigInt, n: &BigInt) -> Option<BigInt> {
460+
use num_integer::*;
461+
let ExtendedGcd { gcd, x: c, .. } = a.extended_gcd(n);
462+
if gcd.is_one() {
463+
Some(normalize(c, n))
464+
} else {
465+
None
466+
}
467+
}
468+
let a = inverse(a % modulus, modulus).ok_or_else(|| {
469+
vm.new_value_error(
470+
"base is not invertible for the given modulus".to_owned(),
471+
)
472+
})?;
473+
let b = -b;
474+
a.modpow(&b, modulus)
450475
} else {
451-
Ok(vm.ctx.new_int(a.modpow(b, modulus)))
452-
}
476+
a.modpow(b, modulus)
477+
};
478+
Ok(vm.ctx.new_int(i))
453479
},
454480
vm,
455481
)

vm/src/builtins/make_module.rs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -594,15 +594,25 @@ mod decl {
594594
}
595595
}
596596

597+
#[derive(FromArgs)]
598+
struct PowArgs {
599+
#[pyarg(any)]
600+
base: PyObjectRef,
601+
#[pyarg(any)]
602+
exp: PyObjectRef,
603+
#[pyarg(any, optional, name = "mod")]
604+
modulus: Option<PyObjectRef>,
605+
}
606+
597607
#[allow(clippy::suspicious_else_formatting)]
598608
#[pyfunction]
599-
fn pow(
600-
x: PyObjectRef,
601-
y: PyObjectRef,
602-
mod_value: OptionalOption<PyObjectRef>,
603-
vm: &VirtualMachine,
604-
) -> PyResult {
605-
match mod_value.flatten() {
609+
fn pow(args: PowArgs, vm: &VirtualMachine) -> PyResult {
610+
let PowArgs {
611+
base: x,
612+
exp: y,
613+
modulus,
614+
} = args;
615+
match modulus {
606616
None => vm.call_or_reflection(&x, &y, "__pow__", "__rpow__", |vm, x, y| {
607617
Err(vm.new_unsupported_binop_error(x, y, "pow"))
608618
}),

vm/src/stdlib/sysconfigdata.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
use crate::pyobject::{ItemProtocol, PyObjectRef};
22
use crate::VirtualMachine;
33

4+
use crate::sysmodule::MULTIARCH;
5+
46
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
57
let vars = vm.ctx.new_dict();
68
macro_rules! hashmap {
7-
($($key:literal => $value:literal),*) => {{
8-
$(vars.set_item($key, vm.ctx.new_str($value.to_owned()), vm).unwrap();)*
9+
($($key:literal => $value:expr),*$(,)?) => {{
10+
$(vars.set_item($key, vm.ctx.new_str($value), vm).unwrap();)*
911
}};
1012
}
13+
hashmap! {
14+
// fake shared module extension
15+
"EXT_SUFFIX" => format!(".rustpython-{}", MULTIARCH),
16+
"MULTIARCH" => MULTIARCH,
17+
}
1118
include!(concat!(env!("OUT_DIR"), "/env_vars.rs"));
1219

1320
py_module!(vm, "_sysconfigdata", {

vm/src/sysmodule.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ const ABIFLAGS: &str = "";
398398
// not the same as CPython (e.g. rust's x86_x64-unknown-linux-gnu is just x86_64-linux-gnu)
399399
// but hopefully that's just an implementation detail? TODO: copy CPython's multiarch exactly,
400400
// https://github.com/python/cpython/blob/3.8/configure.ac#L725
401-
const MULTIARCH: &str = env!("RUSTPYTHON_TARGET_TRIPLE");
401+
pub(crate) const MULTIARCH: &str = env!("RUSTPYTHON_TARGET_TRIPLE");
402402

403403
pub(crate) fn sysconfigdata_name() -> String {
404404
format!("_sysconfigdata_{}_{}_{}", ABIFLAGS, PLATFORM, MULTIARCH)

vm/src/version.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use chrono::Local;
77
use std::time::{Duration, UNIX_EPOCH};
88

99
const MAJOR: usize = 3;
10-
const MINOR: usize = 5;
10+
const MINOR: usize = 9;
1111
const MICRO: usize = 0;
1212
const RELEASELEVEL: &str = "alpha";
1313
const RELEASELEVEL_N: usize = 0xA;

wasm/lib/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# RustPython
22

3-
A Python-3 (CPython >= 3.5.0) Interpreter written in Rust.
3+
A Python-3 (CPython >= 3.8.0) Interpreter written in Rust.
44

55
[![Build Status](https://travis-ci.org/RustPython/RustPython.svg?branch=master)](https://travis-ci.org/RustPython/RustPython)
66
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)

0 commit comments

Comments
 (0)