Skip to content

Commit de8b325

Browse files
authored
Merge pull request RustPython#1854 from youknowone/strbytes-iscased
Fix {bytes|bytearray}.{isascii,islower,isupper}
2 parents 97cdded + 95e938c commit de8b325

File tree

3 files changed

+38
-26
lines changed

3 files changed

+38
-26
lines changed

Lib/test/string_tests.py

Lines changed: 0 additions & 3 deletions
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

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -624,33 +624,47 @@ impl PyByteInner {
624624
}
625625

626626
pub fn isascii(&self) -> bool {
627-
!self.elements.is_empty() && self.elements.iter().all(|x| char::from(*x).is_ascii())
627+
self.elements.iter().all(|x| char::from(*x).is_ascii())
628628
}
629629

630630
pub fn isdigit(&self) -> bool {
631631
!self.elements.is_empty() && self.elements.iter().all(|x| char::from(*x).is_digit(10))
632632
}
633633

634634
pub fn islower(&self) -> bool {
635-
!self.elements.is_empty()
636-
&& self
637-
.elements
638-
.iter()
639-
.filter(|x| !char::from(**x).is_whitespace())
640-
.all(|x| char::from(*x).is_lowercase())
635+
// CPython _Py_bytes_islower
636+
let mut cased = false;
637+
for b in self.elements.iter() {
638+
let c = *b as char;
639+
if c.is_uppercase() {
640+
return false;
641+
} else if !cased && c.is_lowercase() {
642+
cased = true
643+
}
644+
}
645+
cased
641646
}
642647

643-
pub fn isspace(&self) -> bool {
644-
!self.elements.is_empty() && self.elements.iter().all(|x| char::from(*x).is_whitespace())
648+
pub fn isupper(&self) -> bool {
649+
// CPython _Py_bytes_isupper
650+
let mut cased = false;
651+
for b in self.elements.iter() {
652+
let c = *b as char;
653+
if c.is_lowercase() {
654+
return false;
655+
} else if !cased && c.is_uppercase() {
656+
cased = true
657+
}
658+
}
659+
cased
645660
}
646661

647-
pub fn isupper(&self) -> bool {
662+
pub fn isspace(&self) -> bool {
648663
!self.elements.is_empty()
649664
&& self
650665
.elements
651666
.iter()
652-
.filter(|x| !char::from(**x).is_whitespace())
653-
.all(|x| char::from(*x).is_uppercase())
667+
.all(|x| char::from(*x).is_ascii_whitespace())
654668
}
655669

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

vm/src/obj/objstr.rs

Lines changed: 12 additions & 11 deletions
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};
@@ -833,29 +832,31 @@ impl PyString {
833832
!self.value.is_empty() && self.value.chars().all(|c| c.is_ascii_whitespace())
834833
}
835834

836-
// Return true if all cased characters in the string are uppercase and there is at least one cased character, false otherwise.
835+
// Return true if all cased characters in the string are lowercase and there is at least one cased character, false otherwise.
837836
#[pymethod]
838-
fn isupper(&self) -> bool {
837+
fn islower(&self) -> bool {
838+
// CPython unicode_islower_impl
839839
let mut cased = false;
840840
for c in self.value.chars() {
841-
if is_cased(c) && c.is_uppercase() {
842-
cased = true
843-
} else if is_cased(c) && c.is_lowercase() {
841+
if c.is_uppercase() {
844842
return false;
843+
} else if !cased && c.is_lowercase() {
844+
cased = true
845845
}
846846
}
847847
cased
848848
}
849849

850-
// Return true if all cased characters in the string are lowercase and there is at least one cased character, false otherwise.
850+
// Return true if all cased characters in the string are uppercase and there is at least one cased character, false otherwise.
851851
#[pymethod]
852-
fn islower(&self) -> bool {
852+
fn isupper(&self) -> bool {
853+
// CPython unicode_isupper_impl
853854
let mut cased = false;
854855
for c in self.value.chars() {
855-
if is_cased(c) && c.is_lowercase() {
856-
cased = true
857-
} else if is_cased(c) && c.is_uppercase() {
856+
if c.is_lowercase() {
858857
return false;
858+
} else if !cased && c.is_uppercase() {
859+
cased = true
859860
}
860861
}
861862
cased

0 commit comments

Comments
 (0)