Skip to content

Commit 95e938c

Browse files
committed
Fix {str|bytes|bytearray}.{isascii,islower,isupper}
1 parent c7467e4 commit 95e938c

File tree

3 files changed

+38
-26
lines changed

3 files changed

+38
-26
lines changed

Lib/test/string_tests.py

-3
Original file line numberDiff line numberDiff line change
@@ -857,7 +857,6 @@ def test_zfill(self):
857857

858858
self.checkraises(TypeError, '123', 'zfill')
859859

860-
@unittest.skip("TODO: RUSTPYTHON test_bytes")
861860
def test_islower(self):
862861
self.checkequal(False, '', 'islower')
863862
self.checkequal(True, 'a', 'islower')
@@ -868,7 +867,6 @@ def test_islower(self):
868867
self.checkequal(True, 'abc\n', 'islower')
869868
self.checkraises(TypeError, 'abc', 'islower', 42)
870869

871-
@unittest.skip("TODO: RUSTPYTHON test_bytes")
872870
def test_isupper(self):
873871
self.checkequal(False, '', 'isupper')
874872
self.checkequal(False, 'a', 'isupper')
@@ -925,7 +923,6 @@ def test_isalnum(self):
925923
self.checkequal(False, 'abc\n', 'isalnum')
926924
self.checkraises(TypeError, 'abc', 'isalnum', 42)
927925

928-
@unittest.skip("TODO: RUSTPYTHON test_bytes")
929926
def test_isascii(self):
930927
self.checkequal(True, '', 'isascii')
931928
self.checkequal(True, '\x00', 'isascii')

vm/src/obj/objbyteinner.rs

+26-12
Original file line numberDiff line numberDiff line change
@@ -611,33 +611,47 @@ impl PyByteInner {
611611
}
612612

613613
pub fn isascii(&self) -> bool {
614-
!self.elements.is_empty() && self.elements.iter().all(|x| char::from(*x).is_ascii())
614+
self.elements.iter().all(|x| char::from(*x).is_ascii())
615615
}
616616

617617
pub fn isdigit(&self) -> bool {
618618
!self.elements.is_empty() && self.elements.iter().all(|x| char::from(*x).is_digit(10))
619619
}
620620

621621
pub fn islower(&self) -> bool {
622-
!self.elements.is_empty()
623-
&& self
624-
.elements
625-
.iter()
626-
.filter(|x| !char::from(**x).is_whitespace())
627-
.all(|x| char::from(*x).is_lowercase())
622+
// CPython _Py_bytes_islower
623+
let mut cased = false;
624+
for b in self.elements.iter() {
625+
let c = *b as char;
626+
if c.is_uppercase() {
627+
return false;
628+
} else if !cased && c.is_lowercase() {
629+
cased = true
630+
}
631+
}
632+
cased
628633
}
629634

630-
pub fn isspace(&self) -> bool {
631-
!self.elements.is_empty() && self.elements.iter().all(|x| char::from(*x).is_whitespace())
635+
pub fn isupper(&self) -> bool {
636+
// CPython _Py_bytes_isupper
637+
let mut cased = false;
638+
for b in self.elements.iter() {
639+
let c = *b as char;
640+
if c.is_lowercase() {
641+
return false;
642+
} else if !cased && c.is_uppercase() {
643+
cased = true
644+
}
645+
}
646+
cased
632647
}
633648

634-
pub fn isupper(&self) -> bool {
649+
pub fn isspace(&self) -> bool {
635650
!self.elements.is_empty()
636651
&& self
637652
.elements
638653
.iter()
639-
.filter(|x| !char::from(**x).is_whitespace())
640-
.all(|x| char::from(*x).is_uppercase())
654+
.all(|x| char::from(*x).is_ascii_whitespace())
641655
}
642656

643657
pub fn istitle(&self) -> bool {

vm/src/obj/objstr.rs

+12-11
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ use std::string::ToString;
99
use num_traits::ToPrimitive;
1010
use unic::ucd::category::GeneralCategory;
1111
use unic::ucd::ident::{is_xid_continue, is_xid_start};
12-
use unic::ucd::is_cased;
1312
use unicode_casing::CharExt;
1413

1514
use super::objbytes::{PyBytes, PyBytesRef};
@@ -826,29 +825,31 @@ impl PyString {
826825
!self.value.is_empty() && self.value.chars().all(|c| c.is_ascii_whitespace())
827826
}
828827

829-
// Return true if all cased characters in the string are uppercase and there is at least one cased character, false otherwise.
828+
// Return true if all cased characters in the string are lowercase and there is at least one cased character, false otherwise.
830829
#[pymethod]
831-
fn isupper(&self) -> bool {
830+
fn islower(&self) -> bool {
831+
// CPython unicode_islower_impl
832832
let mut cased = false;
833833
for c in self.value.chars() {
834-
if is_cased(c) && c.is_uppercase() {
835-
cased = true
836-
} else if is_cased(c) && c.is_lowercase() {
834+
if c.is_uppercase() {
837835
return false;
836+
} else if !cased && c.is_lowercase() {
837+
cased = true
838838
}
839839
}
840840
cased
841841
}
842842

843-
// Return true if all cased characters in the string are lowercase and there is at least one cased character, false otherwise.
843+
// Return true if all cased characters in the string are uppercase and there is at least one cased character, false otherwise.
844844
#[pymethod]
845-
fn islower(&self) -> bool {
845+
fn isupper(&self) -> bool {
846+
// CPython unicode_isupper_impl
846847
let mut cased = false;
847848
for c in self.value.chars() {
848-
if is_cased(c) && c.is_lowercase() {
849-
cased = true
850-
} else if is_cased(c) && c.is_uppercase() {
849+
if c.is_lowercase() {
851850
return false;
851+
} else if !cased && c.is_uppercase() {
852+
cased = true
852853
}
853854
}
854855
cased

0 commit comments

Comments
 (0)