diff --git a/Lib/test/test_bigmem.py b/Lib/test/test_bigmem.py index ea76b1282..12b221a66 100644 --- a/Lib/test/test_bigmem.py +++ b/Lib/test/test_bigmem.py @@ -789,14 +789,6 @@ class BytesTest(unittest.TestCase, BaseStrTest): def test_swapcase(self, size): self._test_swapcase(size) - @unittest.expectedFailure # TODO: RUSTPYTHON - def test_isspace(self): - return super().test_isspace() - - @unittest.expectedFailure # TODO: RUSTPYTHON - def test_istitle(self): - return super().test_istitle() - class BytearrayTest(unittest.TestCase, BaseStrTest): @@ -823,14 +815,6 @@ class BytearrayTest(unittest.TestCase, BaseStrTest): test_hash = None test_split_large = None - @unittest.expectedFailure # TODO: RUSTPYTHON - def test_isspace(self): - return super().test_isspace() - - @unittest.expectedFailure # TODO: RUSTPYTHON - def test_istitle(self): - return super().test_istitle() - class TupleTest(unittest.TestCase): # Tuples have a small, fixed-sized head and an array of pointers to diff --git a/crates/vm/src/bytes_inner.rs b/crates/vm/src/bytes_inner.rs index d144004d6..6c76808c5 100644 --- a/crates/vm/src/bytes_inner.rs +++ b/crates/vm/src/bytes_inner.rs @@ -397,44 +397,40 @@ impl PyBytesInner { self.elements.py_isupper() } + // _Py_bytes_isspace pub fn isspace(&self) -> bool { + // is_ascii_whitespace excludes vertical tab, while Py_ISSPACE accepts it !self.elements.is_empty() && self .elements .iter() - .all(|x| char::from(*x).is_ascii_whitespace()) + .all(|x| x.is_ascii_whitespace() || *x == b'\x0b') } + // _Py_bytes_istitle pub fn istitle(&self) -> bool { - if self.elements.is_empty() { - return false; - } + let mut cased = false; + let mut previous_is_cased = false; - let mut iter = self.elements.iter().peekable(); - let mut prev_cased = false; - - while let Some(c) = iter.next() { - let current = char::from(*c); - let next = if let Some(k) = iter.peek() { - char::from(**k) - } else if current.is_uppercase() { - return !prev_cased; + for byte in &self.elements { + if byte.is_ascii_uppercase() { + if previous_is_cased { + return false; + } + previous_is_cased = true; + cased = true; + } else if byte.is_ascii_lowercase() { + if !previous_is_cased { + return false; + } + previous_is_cased = true; + cased = true; } else { - return prev_cased; - }; - - let is_cased = current.to_uppercase().next().unwrap() != current - || current.to_lowercase().next().unwrap() != current; - if (is_cased && next.is_uppercase() && !prev_cased) - || (!is_cased && next.is_lowercase()) - { - return false; + previous_is_cased = false; } - - prev_cased = is_cased; } - true + cased } pub fn lower(&self) -> Vec { diff --git a/extra_tests/snippets/builtin_bytes.py b/extra_tests/snippets/builtin_bytes.py index 2cb4c317f..4f8613644 100644 --- a/extra_tests/snippets/builtin_bytes.py +++ b/extra_tests/snippets/builtin_bytes.py @@ -708,3 +708,42 @@ b = B1.fromhex("a0a1a2") assert b.foo == "bar" skip_if_unsupported(3, 11, test__bytes__) + +assert " \f\n\r\t\v".encode("utf-8").isspace() +assert " \f\n\r\t\v".encode("latin-1").isspace() + +# bytes.istitle tests +s = b"Aa6A" +assert s.istitle(), f"{s}" +s = b"Aa6aA" +assert not s.istitle(), f"{s}" +s = b"Python Is Fun" +assert s.istitle(), f"{s}" +s = b"Python is fun" +assert not s.istitle(), f"{s}" +s = b"PYTHON IS FUN" +assert not s.istitle(), f"{s}" +s = b"Python 3.9 Is Awesome!" +assert s.istitle(), f"{s}" +s = b"" +assert not s.istitle(), f"{s}" +s = b"Hello Is Amazing" +assert s.istitle(), f"{s}" +s = b"Not--a Titlecase String" +assert not s.istitle(), f"{s}" +s = b"123A" +assert s.istitle(), f"{s}" +s = b"123a" +assert not s.istitle(), f"{s}" +s = b"123A\ta" +assert not s.istitle(), f"{s}" +SUBSTR = b"123456" +s = b"".join([b"A", b"a" * 64, SUBSTR]) +assert s.istitle(), f"{s}" +s += b"A" +assert s.istitle(), f"{s}" +s += b"aA" +assert not s.istitle(), f"{s}" +assert "123A".istitle(), f"{s}" +assert not "123a".istitle(), f"{s}" +assert not "123A\ta".istitle(), f"{s}"