Skip to content

Commit c31d077

Browse files
committed
pybricks.tools: Add helper for awaitable constants.
We have a few functions with this structure: async def some_func(): if simple_case: # This commits adds a helper to do this: return some_constant return await some_awaitable_operation() Also apply this to the touch sensor value of the Powered Up device.
1 parent b1b544a commit c31d077

3 files changed

Lines changed: 58 additions & 26 deletions

File tree

pybricks/iodevices/pb_type_iodevices_pupdevice.c

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -165,18 +165,6 @@ static mp_obj_t get_pup_data_tuple(mp_obj_t self_in) {
165165
return mp_obj_new_tuple(mode_info[current_mode].num_values, values);
166166
}
167167

168-
static mp_obj_t iodevices_PUPDevice_touch_sensor_true(mp_obj_t self_in) {
169-
return mp_const_true;
170-
}
171-
172-
static mp_obj_t iodevices_PUPDevice_touch_sensor_false(mp_obj_t self_in) {
173-
return mp_const_false;
174-
}
175-
176-
static pbio_error_t iodevices_PUPDevice_touch_sensor_iter_once(pbio_os_state_t *state, mp_obj_t self_in) {
177-
return PBIO_SUCCESS;
178-
}
179-
180168
// pybricks.iodevices.PUPDevice.read
181169
static mp_obj_t iodevices_PUPDevice_read(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
182170
PB_PARSE_ARGS_METHOD(n_args, pos_args, kw_args,
@@ -189,19 +177,7 @@ static mp_obj_t iodevices_PUPDevice_read(size_t n_args, const mp_obj_t *pos_args
189177
if (self->passive_id == LEGO_DEVICE_TYPE_ID_LPF2_TOUCH) {
190178
uint32_t value;
191179
pb_assert(pbio_port_get_analog_value(self->port, self->passive_id, false, &value));
192-
193-
if (!pb_module_tools_run_loop_is_active()) {
194-
return mp_obj_new_bool(value);
195-
}
196-
197-
// REVISIT: we could probably make something more efficient here.
198-
pb_type_async_t config = {
199-
.parent_obj = MP_OBJ_FROM_PTR(self),
200-
.iter_once = iodevices_PUPDevice_touch_sensor_iter_once,
201-
.return_map = value ? iodevices_PUPDevice_touch_sensor_true : iodevices_PUPDevice_touch_sensor_false,
202-
};
203-
204-
return pb_type_async_wait_or_await(&config, &self->device_base.last_awaitable, false);
180+
return pb_type_async_return_result(mp_obj_new_bool(value), &self->device_base.last_awaitable);
205181
}
206182

207183
// Other passive devices don't support reading.

pybricks/tools/pb_type_async.c

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ MP_DEFINE_CONST_OBJ_TYPE(pb_type_async,
103103
* Returns an awaitable operation if the runloop is active, or awaits the
104104
* operation here and now.
105105
*
106-
* @param [in] config Configuration of the operation
106+
* @param [in] config Configuration of the operation. NB: State will not be reset.
107107
* @param [in, out] prev Candidate iterable object that might be re-used, otherwise assigned newly allocated object.
108108
* @param [in] stop_prev Whether to stop ongoing awaitable if it is active.
109109
* @returns An awaitable if the runloop is active, otherwise the mapped return value.
@@ -147,3 +147,57 @@ mp_obj_t pb_type_async_wait_or_await(pb_type_async_t *config, pb_type_async_t **
147147
pb_assert(err);
148148
return config->return_map ? config->return_map(config->parent_obj) : mp_const_none;
149149
}
150+
151+
/**
152+
* Iteration for a constant awaitable that yields once before returning.
153+
*
154+
* This is different from omitting iter_once to achieve a single yield, since
155+
* that special case cannot have a return value.
156+
*
157+
* @param [in] state Protothread state.
158+
* @param [in] parent_obj The constant.
159+
*/
160+
static pbio_error_t pb_type_async_constant_iter_once(pbio_os_state_t *state, mp_obj_t parent_obj) {
161+
PBIO_OS_ASYNC_BEGIN(state);
162+
PBIO_OS_AWAIT_ONCE(state);
163+
PBIO_OS_ASYNC_END(PBIO_SUCCESS);
164+
}
165+
166+
/**
167+
* Return map for a constant awaitable.
168+
*
169+
* @param [in] parent_obj The constant.
170+
* @returns The same constant.
171+
*/
172+
static mp_obj_t pb_type_async_constant_return_map(mp_obj_t parent_obj) {
173+
return parent_obj;
174+
}
175+
176+
/**
177+
* Returns an awaitable operation that yields once and then returns a constant
178+
* result. If the runloop is not active, this just returns the given value.
179+
*
180+
* Can be used to return constants from functions that still need to be
181+
* awaitable for other reasons.
182+
*
183+
* @param [in] result_obj Return result.
184+
* @param [in, out] prev Candidate iterable object that might be
185+
* re-used, otherwise assigned newly allocated object.
186+
* @returns An awaitable if the runloop is active, otherwise the constant return value.
187+
*/
188+
mp_obj_t pb_type_async_return_result(mp_obj_t result_obj, pb_type_async_t **prev) {
189+
190+
// In synchronous mode, return right away.
191+
if (!pb_module_tools_run_loop_is_active()) {
192+
return result_obj;
193+
}
194+
195+
// Async case returns soon.
196+
pb_type_async_t config = {
197+
.parent_obj = result_obj,
198+
.iter_once = pb_type_async_constant_iter_once,
199+
.return_map = pb_type_async_constant_return_map,
200+
.state = 0,
201+
};
202+
return pb_type_async_wait_or_await(&config, prev, false);
203+
}

pybricks/tools/pb_type_async.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ typedef struct {
7575

7676
mp_obj_t pb_type_async_wait_or_await(pb_type_async_t *config, pb_type_async_t **prev, bool stop_prev);
7777

78+
mp_obj_t pb_type_async_return_result(mp_obj_t result_obj, pb_type_async_t **prev);
79+
7880
void pb_type_async_schedule_stop_iteration(pb_type_async_t *iter);
7981

8082
#endif // PYBRICKS_INCLUDED_ASYNC_H

0 commit comments

Comments
 (0)