Skip to content

Commit 1b8706f

Browse files
committed
experimental support for __all__
1 parent 1c19926 commit 1b8706f

File tree

2 files changed

+83
-1
lines changed

2 files changed

+83
-1
lines changed

locale/circuitpython.pot

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ msgstr ""
7272
msgid "%%c needs int or char"
7373
msgstr ""
7474

75+
#: py/runtime.c
76+
msgid "%S '%s' in module %q"
77+
msgstr ""
78+
7579
#: shared-bindings/rgbmatrix/RGBMatrix.c
7680
#, c-format
7781
msgid ""

py/runtime.c

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1687,11 +1687,89 @@ mp_obj_t __attribute__((noinline, )) mp_import_from(mp_obj_t module, qstr name)
16871687
#endif
16881688
}
16891689

1690+
#define IMPORT_ALL_DEBUGGING 0
1691+
1692+
#if IMPORT_ALL_DEBUGGING
1693+
1694+
#define IMPORT_ALL_DEBUG_PRINT_OBJECT(object) mp_obj_print(object, PRINT_REPR);
1695+
#define IMPORT_ALL_DEBUG_PRINTF(fmt, ...) mp_printf(MP_PYTHON_PRINTER, fmt,##__VA_ARGS__)
1696+
1697+
#else
1698+
1699+
#define IMPORT_ALL_DEBUG_PRINT_OBJECT(object) ;
1700+
#define IMPORT_ALL_DEBUG_PRINTF(fmt, ...) ;
1701+
1702+
#endif
1703+
16901704
void mp_import_all(mp_obj_t module) {
16911705
DEBUG_printf("import all %p\n", module);
16921706

1693-
// TODO: Support __all__
16941707
mp_map_t *map = &mp_obj_module_get_globals(module)->map;
1708+
1709+
// check for __all__
1710+
mp_obj_t all_str = MP_OBJ_NEW_QSTR(MP_QSTR___all__);
1711+
IMPORT_ALL_DEBUG_PRINTF("allstr = %s\n", mp_obj_str_get_str(all_str));
1712+
1713+
qstr module_qname = MP_QSTR_empty;
1714+
if (IMPORT_ALL_DEBUGGING) {
1715+
mp_map_elem_t *elem = mp_map_lookup(map, MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_MAP_LOOKUP);
1716+
if (elem != NULL) {
1717+
module_qname = mp_obj_str_get_qstr(elem->value);
1718+
}
1719+
}
1720+
1721+
mp_map_elem_t *all_elem = mp_map_lookup(map, all_str, MP_MAP_LOOKUP);
1722+
if (all_elem != 0 && all_elem->value != MP_OBJ_NULL) {
1723+
// we have __all__ defined, so we only import those symbols
1724+
1725+
mp_obj_t all_obj = all_elem->value;
1726+
IMPORT_ALL_DEBUG_PRINTF("processing import %q with __all__ * allstr = ", module_qname);
1727+
IMPORT_ALL_DEBUG_PRINT_OBJECT(all_obj);
1728+
IMPORT_ALL_DEBUG_PRINTF("\n");
1729+
1730+
// TODO: How should errors be handled?
1731+
// Maybe we should also check in the import logic, not
1732+
// just here? Otherwise we end up with a module that works fine
1733+
// with `import Module` but fails with `from Module import *`.
1734+
// And since ``from Module import *` actually imports the module
1735+
// _before_ calling mp_import_all, you can't simply fix the file
1736+
// and try again (assuming `supervisor.autoreload` is disabled).
1737+
// OTOH, CPython appears to behave them same way, so...
1738+
//
1739+
// would be nice to add the module name to the error message
1740+
#define IMPORT_ALL_KEY_FAILED(errType, message) \
1741+
IMPORT_ALL_DEBUG_PRINTF("CANT_IMPORT_KEY: %s\n", \
1742+
mp_obj_str_get_str(all_key)); \
1743+
/* continue; */ \
1744+
mp_raise_msg_varg(&mp_type_##errType##Error, \
1745+
MP_ERROR_TEXT("%S '%s' in module %q"), \
1746+
MP_ERROR_TEXT(message), \
1747+
mp_obj_str_get_str(all_key), \
1748+
module_qname \
1749+
); \
1750+
1751+
mp_int_t all_len = mp_obj_get_int(mp_obj_len(all_obj));
1752+
for (mp_int_t j = 0; j < all_len; j++) {
1753+
// get each key from __all__
1754+
mp_obj_t all_key = mp_obj_subscr(all_obj, MP_OBJ_NEW_SMALL_INT(j), MP_OBJ_SENTINEL);
1755+
if (!mp_obj_is_str(all_key)) {
1756+
IMPORT_ALL_KEY_FAILED(Type, "__all__ item must be str");
1757+
}
1758+
IMPORT_ALL_DEBUG_PRINTF(" symbol: %s\n", mp_obj_str_get_str(all_key));
1759+
1760+
// find the matching instance in the module
1761+
mp_map_elem_t *all_value = mp_map_lookup(map, all_key, MP_MAP_LOOKUP);
1762+
if (all_value == NULL) {
1763+
IMPORT_ALL_KEY_FAILED(Attribute, "missing __all__ attribute");
1764+
}
1765+
qstr qname = mp_obj_str_get_qstr(all_key);
1766+
mp_store_name(qname, all_value->value);
1767+
}
1768+
return;
1769+
}
1770+
1771+
// no __all__ defined, so we import everything
1772+
IMPORT_ALL_DEBUG_PRINTF("processing from %q import * without __all__", module_qname);
16951773
for (size_t i = 0; i < map->alloc; i++) {
16961774
if (mp_map_slot_is_filled(map, i)) {
16971775
// Entry in module global scope may be generated programmatically

0 commit comments

Comments
 (0)