Skip to content

Commit d77da83

Browse files
committed
py/objrange: Implement (in)equality comparison between range objects.
This feature is not often used so is guarded by the config option MICROPY_PY_BUILTINS_RANGE_BINOP which is disabled by default. With this option disabled MicroPython will always return false when comparing two range objects for equality (unless they are exactly the same object instance). This does not match CPython so if (in)equality between range objects is needed then this option should be enabled. Enabling this option costs between 100 and 200 bytes of code space depending on the machine architecture.
1 parent 5604b71 commit d77da83

3 files changed

Lines changed: 61 additions & 0 deletions

File tree

py/mpconfig.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,14 @@ typedef double mp_float_t;
787787
#define MICROPY_PY_BUILTINS_RANGE_ATTRS (1)
788788
#endif
789789

790+
// Whether to support binary ops [only (in)equality is defined] between range
791+
// objects. With this option disabled all range objects that are not exactly
792+
// the same object will compare as not-equal. With it enabled the semantics
793+
// match CPython and ranges are equal if they yield the same sequence of items.
794+
#ifndef MICROPY_PY_BUILTINS_RANGE_BINOP
795+
#define MICROPY_PY_BUILTINS_RANGE_BINOP (0)
796+
#endif
797+
790798
// Whether to support timeout exceptions (like socket.timeout)
791799
#ifndef MICROPY_PY_BUILTINS_TIMEOUTERROR
792800
#define MICROPY_PY_BUILTINS_TIMEOUTERROR (0)

py/objrange.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,24 @@ STATIC mp_obj_t range_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
138138
}
139139
}
140140

141+
#if MICROPY_PY_BUILTINS_RANGE_BINOP
142+
STATIC mp_obj_t range_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
143+
if (!MP_OBJ_IS_TYPE(rhs_in, &mp_type_range) || op != MP_BINARY_OP_EQUAL) {
144+
return MP_OBJ_NULL; // op not supported
145+
}
146+
mp_obj_range_t *lhs = MP_OBJ_TO_PTR(lhs_in);
147+
mp_obj_range_t *rhs = MP_OBJ_TO_PTR(rhs_in);
148+
mp_int_t lhs_len = range_len(lhs);
149+
mp_int_t rhs_len = range_len(rhs);
150+
return mp_obj_new_bool(
151+
lhs_len == rhs_len
152+
&& (lhs_len == 0
153+
|| (lhs->start == rhs->start
154+
&& (lhs_len == 1 || lhs->step == rhs->step)))
155+
);
156+
}
157+
#endif
158+
141159
STATIC mp_obj_t range_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
142160
if (value == MP_OBJ_SENTINEL) {
143161
// load
@@ -195,6 +213,9 @@ const mp_obj_type_t mp_type_range = {
195213
.print = range_print,
196214
.make_new = range_make_new,
197215
.unary_op = range_unary_op,
216+
#if MICROPY_PY_BUILTINS_RANGE_BINOP
217+
.binary_op = range_binary_op,
218+
#endif
198219
.subscr = range_subscr,
199220
.getiter = range_getiter,
200221
#if MICROPY_PY_BUILTINS_RANGE_ATTRS
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# test binary operations on range objects; (in)equality only
2+
3+
# this "feature test" actually tests the implementation but is the best we can do
4+
if range(1) != range(1):
5+
print("SKIP")
6+
raise SystemExit
7+
8+
# basic (in)equality
9+
print(range(1) == range(1))
10+
print(range(1) != range(1))
11+
print(range(1) != range(2))
12+
13+
# empty range
14+
print(range(0) == range(0))
15+
print(range(1, 0) == range(0))
16+
print(range(1, 4, -1) == range(6, 3))
17+
18+
# 1 element range
19+
print(range(1, 4, 10) == range(1, 4, 10))
20+
print(range(1, 4, 10) == range(1, 4, 20))
21+
print(range(1, 4, 10) == range(1, 8, 20))
22+
23+
# more than 1 element
24+
print(range(0, 3, 2) == range(0, 3, 2))
25+
print(range(0, 3, 2) == range(0, 4, 2))
26+
print(range(0, 3, 2) == range(0, 5, 2))
27+
28+
# unsupported binary op
29+
try:
30+
range(1) + 10
31+
except TypeError:
32+
print('TypeError')

0 commit comments

Comments
 (0)