From 494fc437118acf1ce989249cfdfcc98cd9c4f02b Mon Sep 17 00:00:00 2001 From: Hackerwins Date: Sat, 22 Jan 2022 14:51:23 +0900 Subject: [PATCH 1/3] Use staticmethod in macros --- Lib/test/test_decorators.py | 1 + derive/src/pyclass.rs | 19 +++++++++++-------- derive/src/util.rs | 1 + vm/src/builtins/builtinfunc.rs | 10 +++++++++- vm/src/builtins/staticmethod.rs | 14 +++++++++++--- 5 files changed, 33 insertions(+), 12 deletions(-) diff --git a/Lib/test/test_decorators.py b/Lib/test/test_decorators.py index 29c80fe4d..b5deb0398 100644 --- a/Lib/test/test_decorators.py +++ b/Lib/test/test_decorators.py @@ -76,6 +76,7 @@ class TestDecorators(unittest.TestCase): self.assertEqual(C.foo(), 42) self.assertEqual(C().foo(), 42) + @unittest.skip("TODO: 3.10.x changed staticmethod to be callable") def test_staticmethod_function(self): @staticmethod def notamethod(x): diff --git a/derive/src/pyclass.rs b/derive/src/pyclass.rs index b148e951a..7a634d5a6 100644 --- a/derive/src/pyclass.rs +++ b/derive/src/pyclass.rs @@ -437,8 +437,9 @@ where let build_func = match self.method_type.as_str() { "method" => quote!(.build_method(ctx, class.clone())), "classmethod" => quote!(.build_classmethod(ctx, class.clone())), + "staticmethod" => quote!(.build_staticmethod(ctx, class.clone())), other => unreachable!( - "Only 'method' and 'classmethod' are supported, got {}", + "Only 'method', 'classmethod' and 'staticmethod' are supported, got {}", other ), }; @@ -954,13 +955,15 @@ where { assert!(ALL_ALLOWED_NAMES.contains(&attr_name.as_str())); Ok(match attr_name.as_str() { - attr_name @ "pymethod" | attr_name @ "pyclassmethod" => Box::new(MethodItem { - inner: ContentItemInner { - index, - attr_name: attr_name.to_owned(), - }, - method_type: attr_name.strip_prefix("py").unwrap().to_owned(), - }), + attr_name @ "pymethod" | attr_name @ "pyclassmethod" | attr_name @ "pystaticmethod" => { + Box::new(MethodItem { + inner: ContentItemInner { + index, + attr_name: attr_name.to_owned(), + }, + method_type: attr_name.strip_prefix("py").unwrap().to_owned(), + }) + } "pyproperty" => Box::new(PropertyItem { inner: ContentItemInner { index, attr_name }, }), diff --git a/derive/src/util.rs b/derive/src/util.rs index 552dbc01c..0f645598c 100644 --- a/derive/src/util.rs +++ b/derive/src/util.rs @@ -14,6 +14,7 @@ use syn_ext::{ pub(crate) const ALL_ALLOWED_NAMES: &[&str] = &[ "pymethod", "pyclassmethod", + "pystaticmethod", "pyproperty", "pyfunction", "pyclass", diff --git a/vm/src/builtins/builtinfunc.rs b/vm/src/builtins/builtinfunc.rs index a10c46061..c16284bea 100644 --- a/vm/src/builtins/builtinfunc.rs +++ b/vm/src/builtins/builtinfunc.rs @@ -1,4 +1,4 @@ -use super::{pytype, PyClassMethod, PyStr, PyStrRef, PyTypeRef}; +use super::{pytype, PyClassMethod, PyStaticMethod, PyStr, PyStrRef, PyTypeRef}; use crate::{ builtins::PyBoundMethod, function::{FuncArgs, IntoPyNativeFunc, PyNativeFunc}, @@ -45,6 +45,14 @@ impl PyNativeFuncDef { let callable = self.build_method(ctx, class).into(); PyClassMethod::new_ref(callable, ctx) } + pub fn build_staticmethod(self, ctx: &PyContext, class: PyTypeRef) -> PyRef { + let callable = self.build_method(ctx, class).into(); + PyRef::new_ref( + PyStaticMethod { callable }, + ctx.types.staticmethod_type.clone(), + None, + ) + } } #[pyclass(name = "builtin_function_or_method", module = false)] diff --git a/vm/src/builtins/staticmethod.rs b/vm/src/builtins/staticmethod.rs index 038b93b16..37ff1193f 100644 --- a/vm/src/builtins/staticmethod.rs +++ b/vm/src/builtins/staticmethod.rs @@ -1,8 +1,8 @@ use super::{PyStr, PyTypeRef}; use crate::{ builtins::builtinfunc::PyBuiltinMethod, - function::IntoPyNativeFunc, - types::{Constructor, GetDescriptor}, + function::{FuncArgs, IntoPyNativeFunc}, + types::{Callable, Constructor, GetDescriptor}, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, VirtualMachine, }; @@ -59,7 +59,7 @@ impl PyStaticMethod { } } -#[pyimpl(with(GetDescriptor, Constructor), flags(BASETYPE, HAS_DICT))] +#[pyimpl(with(Callable, GetDescriptor, Constructor), flags(BASETYPE, HAS_DICT))] impl PyStaticMethod { #[pyproperty(magic)] fn isabstractmethod(&self, vm: &VirtualMachine) -> PyObjectRef { @@ -76,6 +76,14 @@ impl PyStaticMethod { } } +impl Callable for PyStaticMethod { + type Args = FuncArgs; + #[inline] + fn call(zelf: &crate::PyObjectView, args: FuncArgs, vm: &VirtualMachine) -> PyResult { + vm.invoke(&zelf.callable, args) + } +} + pub fn init(context: &PyContext) { PyStaticMethod::extend_class(context, &context.types.staticmethod_type); } From c36c90beab1ae55c8a16776843e0728f7f138234 Mon Sep 17 00:00:00 2001 From: Hackerwins Date: Sat, 22 Jan 2022 14:57:10 +0900 Subject: [PATCH 2/3] Apply staticmethod to maketrans https://docs.python.org/3.10/library/stdtypes.html?highlight=maketrans#str.maketrans --- vm/src/builtins/bytearray.rs | 2 +- vm/src/builtins/bytes.rs | 2 +- vm/src/builtins/pystr.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/vm/src/builtins/bytearray.rs b/vm/src/builtins/bytearray.rs index e2cfb9788..64e851032 100644 --- a/vm/src/builtins/bytearray.rs +++ b/vm/src/builtins/bytearray.rs @@ -227,7 +227,7 @@ impl PyByteArray { } } - #[pymethod] + #[pystaticmethod] fn maketrans(from: PyBytesInner, to: PyBytesInner, vm: &VirtualMachine) -> PyResult> { PyBytesInner::maketrans(from, to, vm) } diff --git a/vm/src/builtins/bytes.rs b/vm/src/builtins/bytes.rs index 73f612d07..55298b703 100644 --- a/vm/src/builtins/bytes.rs +++ b/vm/src/builtins/bytes.rs @@ -149,7 +149,7 @@ impl PyBytes { self.inner.contains(needle, vm) } - #[pymethod] + #[pystaticmethod] fn maketrans(from: PyBytesInner, to: PyBytesInner, vm: &VirtualMachine) -> PyResult> { PyBytesInner::maketrans(from, to, vm) } diff --git a/vm/src/builtins/pystr.rs b/vm/src/builtins/pystr.rs index ac9c8511d..57d79c87d 100644 --- a/vm/src/builtins/pystr.rs +++ b/vm/src/builtins/pystr.rs @@ -1141,7 +1141,7 @@ impl PyStr { Ok(translated) } - #[pymethod] + #[pystaticmethod] fn maketrans( dict_or_str: PyObjectRef, to_str: OptionalArg, From f46dd59c91bb97163acd8f804546ef6bd0f8da2f Mon Sep 17 00:00:00 2001 From: Hackerwins Date: Sat, 22 Jan 2022 15:19:55 +0900 Subject: [PATCH 3/3] Remove unexpected successes in test_shlex --- Lib/test/test_shlex.py | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/Lib/test/test_shlex.py b/Lib/test/test_shlex.py index bec053a2a..c3d632a64 100644 --- a/Lib/test/test_shlex.py +++ b/Lib/test/test_shlex.py @@ -183,8 +183,6 @@ class ShlexTest(unittest.TestCase): "%s: %s != %s" % (self.data[i][0], l, self.data[i][1:])) - # TODO: RUSTPYTHON - @unittest.expectedFailure def testSyntaxSplitAmpersandAndPipe(self): """Test handling of syntax splitting of &, |""" # Could take these forms: &&, &, |&, ;&, ;;& @@ -202,8 +200,6 @@ class ShlexTest(unittest.TestCase): self.assertEqual(ref, result, "While splitting '%s' [ws=%s]" % (ss, ws)) - # TODO: RUSTPYTHON - @unittest.expectedFailure def testSyntaxSplitSemicolon(self): """Test handling of syntax splitting of ;""" # Could take these forms: ;, ;;, ;&, ;;& @@ -220,8 +216,6 @@ class ShlexTest(unittest.TestCase): self.assertEqual(ref, result, "While splitting '%s' [ws=%s]" % (ss, ws)) - # TODO: RUSTPYTHON - @unittest.expectedFailure def testSyntaxSplitRedirect(self): """Test handling of syntax splitting of >""" # of course, the same applies to <, | @@ -237,8 +231,6 @@ class ShlexTest(unittest.TestCase): self.assertEqual(ref, result, "While splitting '%s' [ws=%s]" % (ss, ws)) - # TODO: RUSTPYTHON - @unittest.expectedFailure def testSyntaxSplitParen(self): """Test handling of syntax splitting of ()""" # these should all parse to the same output @@ -252,8 +244,6 @@ class ShlexTest(unittest.TestCase): self.assertEqual(ref, result, "While splitting '%s' [ws=%s]" % (ss, ws)) - # TODO: RUSTPYTHON - @unittest.expectedFailure def testSyntaxSplitCustom(self): """Test handling of syntax splitting with custom chars""" ss = "~/a&&b-c --color=auto||d *.py?" @@ -267,8 +257,6 @@ class ShlexTest(unittest.TestCase): result = list(s) self.assertEqual(ref, result, "While splitting '%s' [ws=True]" % ss) - # TODO: RUSTPYTHON - @unittest.expectedFailure def testTokenTypes(self): """Test that tokens are split with types as expected.""" for source, expected in ( @@ -289,16 +277,12 @@ class ShlexTest(unittest.TestCase): observed.append((t, tt)) self.assertEqual(observed, expected) - # TODO: RUSTPYTHON - @unittest.expectedFailure def testPunctuationInWordChars(self): """Test that any punctuation chars are removed from wordchars""" s = shlex.shlex('a_b__c', punctuation_chars='_') self.assertNotIn('_', s.wordchars) self.assertEqual(list(s), ['a', '_', 'b', '__', 'c']) - # TODO: RUSTPYTHON - @unittest.expectedFailure def testPunctuationWithWhitespaceSplit(self): """Test that with whitespace_split, behaviour is as expected""" s = shlex.shlex('a && b || c', punctuation_chars='&') @@ -311,8 +295,6 @@ class ShlexTest(unittest.TestCase): # white space self.assertEqual(list(s), ['a', '&&', 'b', '||', 'c']) - # TODO: RUSTPYTHON - @unittest.expectedFailure def testPunctuationWithPosix(self): """Test that punctuation_chars and posix behave correctly together.""" # see Issue #29132 @@ -321,8 +303,6 @@ class ShlexTest(unittest.TestCase): s = shlex.shlex('f >\\"abc\\"', posix=True, punctuation_chars=True) self.assertEqual(list(s), ['f', '>', '"abc"']) - # TODO: RUSTPYTHON - @unittest.expectedFailure def testEmptyStringHandling(self): """Test that parsing of empty strings is correctly handled.""" # see Issue #21999 @@ -386,8 +366,6 @@ class ShlexTest(unittest.TestCase): resplit = shlex.split(joined) self.assertEqual(split_command, resplit) - # TODO: RUSTPYTHON - @unittest.expectedFailure def testPunctuationCharsReadOnly(self): punctuation_chars = "/|$%^" shlex_instance = shlex.shlex(punctuation_chars=punctuation_chars)