Skip to content

Commit 62cad73

Browse files
Merge pull request RustPython#364 from HomerMcMillan/string_is_methods
Fix string is* methods
2 parents 2c693d9 + 00ef668 commit 62cad73

File tree

3 files changed

+224
-33
lines changed

3 files changed

+224
-33
lines changed

tests/snippets/bytearray.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#__getitem__ not implemented yet
2+
#a = bytearray(b'abc')
3+
#assert a[0] == b'a'
4+
#assert a[1] == b'b'
5+
6+
assert len(bytearray([1,2,3])) == 3
7+
8+
assert bytearray(b'1a23').isalnum()
9+
assert not bytearray(b'1%a23').isalnum()
10+
11+
assert bytearray(b'abc').isalpha()
12+
assert not bytearray(b'abc1').isalpha()
13+
14+
# travis doesn't like this
15+
#assert bytearray(b'xyz').isascii()
16+
#assert not bytearray([128, 157, 32]).isascii()
17+
18+
assert bytearray(b'1234567890').isdigit()
19+
assert not bytearray(b'12ab').isdigit()
20+
21+
assert bytearray(b'lower').islower()
22+
assert not bytearray(b'Super Friends').islower()
23+
24+
assert bytearray(b' \n\t').isspace()
25+
assert not bytearray(b'\td\n').isspace()
26+
27+
assert bytearray(b'UPPER').isupper()
28+
assert not bytearray(b'tuPpEr').isupper()
29+
30+
assert bytearray(b'Is Title Case').istitle()
31+
assert not bytearray(b'is Not title casE').istitle()

vm/src/obj/objbytearray.rs

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,46 @@ pub fn init(context: &PyContext) {
3737
"__len__",
3838
context.new_rustfunc(bytesarray_len),
3939
);
40+
context.set_attr(
41+
&bytearray_type,
42+
"isalnum",
43+
context.new_rustfunc(bytearray_isalnum),
44+
);
45+
context.set_attr(
46+
&bytearray_type,
47+
"isalpha",
48+
context.new_rustfunc(bytearray_isalpha),
49+
);
50+
context.set_attr(
51+
&bytearray_type,
52+
"isascii",
53+
context.new_rustfunc(bytearray_isascii),
54+
);
55+
context.set_attr(
56+
&bytearray_type,
57+
"isdigit",
58+
context.new_rustfunc(bytearray_isdigit),
59+
);
60+
context.set_attr(
61+
&bytearray_type,
62+
"islower",
63+
context.new_rustfunc(bytearray_islower),
64+
);
65+
context.set_attr(
66+
&bytearray_type,
67+
"isspace",
68+
context.new_rustfunc(bytearray_isspace),
69+
);
70+
context.set_attr(
71+
&bytearray_type,
72+
"isupper",
73+
context.new_rustfunc(bytearray_isupper),
74+
);
75+
context.set_attr(
76+
&bytearray_type,
77+
"istitle",
78+
context.new_rustfunc(bytearray_istitle),
79+
);
4080
}
4181

4282
fn bytearray_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
@@ -89,6 +129,99 @@ fn bytearray_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
89129
Ok(vm.ctx.new_bool(result))
90130
}
91131

132+
fn bytearray_isalnum(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
133+
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]);
134+
let bytes = get_value(zelf);
135+
Ok(vm.new_bool(!bytes.is_empty() && bytes.iter().all(|x| char::from(*x).is_alphanumeric())))
136+
}
137+
138+
fn bytearray_isalpha(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
139+
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]);
140+
let bytes = get_value(zelf);
141+
Ok(vm.new_bool(!bytes.is_empty() && bytes.iter().all(|x| char::from(*x).is_alphabetic())))
142+
}
143+
144+
fn bytearray_isascii(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
145+
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]);
146+
let bytes = get_value(zelf);
147+
Ok(vm.new_bool(!bytes.is_empty() && bytes.iter().all(|x| char::from(*x).is_ascii())))
148+
}
149+
150+
fn bytearray_isdigit(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
151+
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]);
152+
let bytes = get_value(zelf);
153+
Ok(vm.new_bool(!bytes.is_empty() && bytes.iter().all(|x| char::from(*x).is_digit(10))))
154+
}
155+
156+
fn bytearray_islower(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
157+
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]);
158+
let bytes = get_value(zelf);
159+
Ok(vm.new_bool(
160+
!bytes.is_empty()
161+
&& bytes
162+
.iter()
163+
.filter(|x| char::from(**x).is_whitespace())
164+
.all(|x| char::from(*x).is_lowercase()),
165+
))
166+
}
167+
168+
fn bytearray_isspace(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
169+
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]);
170+
let bytes = get_value(zelf);
171+
Ok(vm.new_bool(!bytes.is_empty() && bytes.iter().all(|x| char::from(*x).is_whitespace())))
172+
}
173+
174+
fn bytearray_isupper(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
175+
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]);
176+
let bytes = get_value(zelf);
177+
Ok(vm.new_bool(
178+
!bytes.is_empty()
179+
&& bytes
180+
.iter()
181+
.filter(|x| !char::from(**x).is_whitespace())
182+
.all(|x| char::from(*x).is_uppercase()),
183+
))
184+
}
185+
186+
fn bytearray_istitle(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
187+
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]);
188+
let bytes = get_value(zelf);
189+
190+
if bytes.is_empty() {
191+
Ok(vm.new_bool(false))
192+
} else {
193+
let mut iter = bytes.iter().peekable();
194+
let mut prev_cased = false;
195+
196+
while let Some(c) = iter.next() {
197+
let current = char::from(*c);
198+
let next = if let Some(k) = iter.peek() {
199+
char::from(**k)
200+
} else {
201+
if current.is_uppercase() {
202+
return Ok(vm.new_bool(!prev_cased));
203+
} else {
204+
return Ok(vm.new_bool(prev_cased));
205+
}
206+
};
207+
208+
if is_cased(current) && next.is_uppercase() && !prev_cased {
209+
return Ok(vm.new_bool(false));
210+
} else if !is_cased(current) && next.is_lowercase() {
211+
return Ok(vm.new_bool(false));
212+
}
213+
prev_cased = is_cased(current);
214+
}
215+
216+
Ok(vm.new_bool(true))
217+
}
218+
}
219+
220+
// helper function for istitle
221+
fn is_cased(c: char) -> bool {
222+
c.to_uppercase().next().unwrap() != c || c.to_lowercase().next().unwrap() != c
223+
}
224+
92225
/*
93226
fn bytearray_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
94227
arg_check!(

vm/src/obj/objstr.rs

Lines changed: 60 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -464,26 +464,34 @@ fn str_isidentifier(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
464464
// which is why isspace is using is_ascii_whitespace. Same for isupper & islower
465465
fn str_isspace(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
466466
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
467-
let is_whitespace = get_value(&s).chars().all(|c| c.is_ascii_whitespace());
468-
Ok(vm.ctx.new_bool(is_whitespace))
467+
let value = get_value(&s);
468+
Ok(vm
469+
.ctx
470+
.new_bool(!value.is_empty() && value.chars().all(|c| c.is_ascii_whitespace())))
469471
}
470472

471473
fn str_isupper(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
472474
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
473-
let is_upper = get_value(&s)
474-
.chars()
475-
.filter(|x| !x.is_ascii_whitespace())
476-
.all(|c| c.is_uppercase());
477-
Ok(vm.ctx.new_bool(is_upper))
475+
let value = get_value(&s);
476+
Ok(vm.ctx.new_bool(
477+
!value.is_empty()
478+
&& value
479+
.chars()
480+
.filter(|x| !x.is_ascii_whitespace())
481+
.all(|c| c.is_uppercase()),
482+
))
478483
}
479484

480485
fn str_islower(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
481486
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
482-
let is_lower = get_value(&s)
483-
.chars()
484-
.filter(|x| !x.is_ascii_whitespace())
485-
.all(|c| c.is_lowercase());
486-
Ok(vm.ctx.new_bool(is_lower))
487+
let value = get_value(&s);
488+
Ok(vm.ctx.new_bool(
489+
!value.is_empty()
490+
&& value
491+
.chars()
492+
.filter(|x| !x.is_ascii_whitespace())
493+
.all(|c| c.is_lowercase()),
494+
))
487495
}
488496

489497
// doesn't implement keep new line delimeter just yet
@@ -785,10 +793,15 @@ fn str_istitle(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
785793
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
786794
let value = get_value(&s);
787795
let mut is_titled = true;
788-
for word in value.split(" ") {
789-
if word != make_title(&word) {
790-
is_titled = false;
791-
break;
796+
797+
if value.is_empty() {
798+
is_titled = false;
799+
} else {
800+
for word in value.split(" ") {
801+
if word != make_title(&word) {
802+
is_titled = false;
803+
break;
804+
}
792805
}
793806
}
794807
Ok(vm.ctx.new_bool(is_titled))
@@ -845,14 +858,18 @@ fn str_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
845858

846859
fn str_isalnum(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
847860
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
848-
let is_alnum = get_value(&s).chars().all(|c| c.is_alphanumeric());
849-
Ok(vm.ctx.new_bool(is_alnum))
861+
let value = get_value(&s);
862+
Ok(vm
863+
.ctx
864+
.new_bool(!value.is_empty() && value.chars().all(|c| c.is_alphanumeric())))
850865
}
851866

852867
fn str_isascii(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
853868
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
854-
let is_ascii = get_value(&s).chars().all(|c| c.is_ascii());
855-
Ok(vm.ctx.new_bool(is_ascii))
869+
let value = get_value(&s);
870+
Ok(vm
871+
.ctx
872+
.new_bool(!value.is_empty() && value.chars().all(|c| c.is_ascii())))
856873
}
857874

858875
fn str_rindex(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
@@ -905,14 +922,18 @@ fn str_rfind(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
905922

906923
fn str_isnumeric(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
907924
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
908-
let is_numeric = get_value(&s).chars().all(|c| c.is_numeric());
909-
Ok(vm.ctx.new_bool(is_numeric))
925+
let value = get_value(&s);
926+
Ok(vm
927+
.ctx
928+
.new_bool(!value.is_empty() && value.chars().all(|c| c.is_numeric())))
910929
}
911930

912931
fn str_isalpha(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
913932
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
914-
let is_alpha = get_value(&s).chars().all(|c| c.is_alphanumeric());
915-
Ok(vm.ctx.new_bool(is_alpha))
933+
let value = get_value(&s);
934+
Ok(vm
935+
.ctx
936+
.new_bool(!value.is_empty() && value.chars().all(|c| c.is_alphanumeric())))
916937
}
917938

918939
fn str_isdigit(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
@@ -923,18 +944,24 @@ fn str_isdigit(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
923944
0x2070, 0x00B9, 0x00B2, 0x00B3, 0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079,
924945
];
925946
let mut is_digit: bool = true;
926-
for c in value.chars() {
927-
if !c.is_digit(10) {
928-
// checking if char is exponent
929-
let char_as_uni: u16 = c as u16;
930-
if valid_unicodes.contains(&char_as_uni) {
931-
continue;
932-
} else {
933-
is_digit = false;
934-
break;
947+
948+
if value.is_empty() {
949+
is_digit = false;
950+
} else {
951+
for c in value.chars() {
952+
if !c.is_digit(10) {
953+
// checking if char is exponent
954+
let char_as_uni: u16 = c as u16;
955+
if valid_unicodes.contains(&char_as_uni) {
956+
continue;
957+
} else {
958+
is_digit = false;
959+
break;
960+
}
935961
}
936962
}
937963
}
964+
938965
Ok(vm.ctx.new_bool(is_digit))
939966
}
940967

0 commit comments

Comments
 (0)