Skip to content

Commit 0ffa06a

Browse files
committed
Two enhancements for launcher
1. Set working directory for subsequent file. 2. Add `pathlib` for easy file discovery.
1 parent 63815c5 commit 0ffa06a

File tree

18 files changed

+815
-11
lines changed

18 files changed

+815
-11
lines changed

.gitmodules

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@
173173
[submodule "ports/raspberrypi/sdk"]
174174
path = ports/raspberrypi/sdk
175175
url = https://github.com/adafruit/pico-sdk.git
176-
branch = force_inline_critical_section
176+
branch = force_inline_critical_section_2.1.1
177177
[submodule "data/nvm.toml"]
178178
path = data/nvm.toml
179179
url = https://github.com/adafruit/nvm.toml.git

extmod/vfs_fat.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838

3939
// CIRCUITPY-CHANGE: extra includes
4040
#include <string.h>
41+
#include "py/gc.h"
4142
#include "py/obj.h"
4243
#include "py/objproperty.h"
4344
#include "py/runtime.h"
@@ -342,7 +343,10 @@ static mp_obj_t fat_vfs_stat(mp_obj_t vfs_in, mp_obj_t path_in) {
342343
FRESULT res = f_stat(&self->fatfs, path, &fno);
343344
if (res != FR_OK) {
344345
// CIRCUITPY-CHANGE
345-
mp_raise_OSError_fresult(res);
346+
if (gc_alloc_possible()) {
347+
mp_raise_OSError_fresult(res);
348+
}
349+
return mp_const_none;
346350
}
347351
}
348352

main.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -457,14 +457,19 @@ static bool __attribute__((noinline)) run_code_py(safe_mode_t safe_mode, bool *s
457457
usb_setup_with_vm();
458458
#endif
459459

460+
// Always return to root before trying to run files.
461+
common_hal_os_chdir("/");
460462
// Check if a different run file has been allocated
461463
if (next_code_configuration != NULL) {
462464
next_code_configuration->options &= ~SUPERVISOR_NEXT_CODE_OPT_NEWLY_SET;
463465
next_code_options = next_code_configuration->options;
464466
if (next_code_configuration->filename[0] != '\0') {
467+
if (next_code_configuration->working_directory != NULL) {
468+
common_hal_os_chdir(next_code_configuration->working_directory);
469+
}
465470
// This is where the user's python code is actually executed:
466471
const char *const filenames[] = { next_code_configuration->filename };
467-
found_main = maybe_run_list(filenames, MP_ARRAY_SIZE(filenames));
472+
found_main = maybe_run_list(filenames, 1);
468473
if (!found_main) {
469474
serial_write(next_code_configuration->filename);
470475
serial_write_compressed(MP_ERROR_TEXT(" not found.\n"));
@@ -1109,9 +1114,6 @@ int __attribute__((used)) main(void) {
11091114
}
11101115
simulate_reset = false;
11111116

1112-
// Always return to root before trying to run files.
1113-
common_hal_os_chdir("/");
1114-
11151117
if (pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL) {
11161118
// If code.py did a fake deep sleep, pretend that we
11171119
// are running code.py for the first time after a hard

ports/atmel-samd/mpconfigport.mk

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ CIRCUITPY_JSON ?= 0
4747
CIRCUITPY_KEYPAD ?= 0
4848
CIRCUITPY_MSGPACK ?= 0
4949
CIRCUITPY_OS_GETENV ?= 0
50+
CIRCUITPY_PATHLIB ?= 0
5051
CIRCUITPY_PIXELMAP ?= 0
5152
CIRCUITPY_RE ?= 0
5253
CIRCUITPY_SDCARDIO ?= 0
@@ -120,6 +121,7 @@ CIRCUITPY_BITMAPFILTER ?= 0
120121
CIRCUITPY_FLOPPYIO ?= $(CIRCUITPY_FULL_BUILD)
121122
CIRCUITPY_FRAMEBUFFERIO ?= $(CIRCUITPY_FULL_BUILD)
122123
CIRCUITPY_MAX3421E ?= $(HAS_1MB_FLASH)
124+
CIRCUITPY_PATHLIB ?= 0
123125
CIRCUITPY_PS2IO ?= 1
124126
CIRCUITPY_RGBMATRIX ?= $(CIRCUITPY_FRAMEBUFFERIO)
125127
CIRCUITPY_SAMD ?= 1

py/circuitpy_defns.mk

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,9 @@ endif
303303
ifeq ($(CIRCUITPY_OS),1)
304304
SRC_PATTERNS += os/%
305305
endif
306+
ifeq ($(CIRCUITPY_PATHLIB),1)
307+
SRC_PATTERNS += pathlib/%
308+
endif
306309
ifeq ($(CIRCUITPY_DUALBANK),1)
307310
SRC_PATTERNS += dualbank/%
308311
endif
@@ -744,6 +747,8 @@ SRC_SHARED_MODULE_ALL = \
744747
onewireio/__init__.c \
745748
onewireio/OneWire.c \
746749
os/__init__.c \
750+
pathlib/__init__.c \
751+
pathlib/PosixPath.c \
747752
paralleldisplaybus/ParallelBus.c \
748753
qrio/__init__.c \
749754
qrio/QRDecoder.c \

py/circuitpy_mpconfig.mk

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,9 @@ CFLAGS += -DCIRCUITPY_OPT_MAP_LOOKUP_CACHE=$(CIRCUITPY_OPT_MAP_LOOKUP_CACHE)
410410
CIRCUITPY_OS ?= 1
411411
CFLAGS += -DCIRCUITPY_OS=$(CIRCUITPY_OS)
412412

413+
CIRCUITPY_PATHLIB ?= $(CIRCUITPY_FULL_BUILD)
414+
CFLAGS += -DCIRCUITPY_PATHLIB=$(CIRCUITPY_PATHLIB)
415+
413416
CIRCUITPY_PEW ?= 0
414417
CFLAGS += -DCIRCUITPY_PEW=$(CIRCUITPY_PEW)
415418

shared-bindings/pathlib/PosixPath.c

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
// This file is part of the CircuitPython project: https://circuitpython.org
2+
//
3+
// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries
4+
//
5+
// SPDX-License-Identifier: MIT
6+
7+
#include <string.h>
8+
9+
#include "py/obj.h"
10+
#include "py/objproperty.h"
11+
#include "py/runtime.h"
12+
#include "py/mperrno.h"
13+
#include "py/mphal.h"
14+
#include "py/gc.h"
15+
#include "py/objstr.h"
16+
#include "py/objtuple.h"
17+
#include "py/objtype.h"
18+
19+
#include "shared-bindings/pathlib/PosixPath.h"
20+
#include "shared-module/pathlib/PosixPath.h"
21+
22+
//| class PosixPath:
23+
//| """Object representing a path on Posix systems"""
24+
//|
25+
//| def __init__(self, path: Union[str, "PosixPath"]) -> None:
26+
//| """Construct a PosixPath object from a string or another PosixPath object.
27+
//|
28+
//| :param path: A string or PosixPath object representing a path
29+
//| """
30+
//| ...
31+
//|
32+
33+
static mp_obj_t pathlib_posixpath_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
34+
mp_arg_check_num(n_args, n_kw, 1, 1, false);
35+
return common_hal_pathlib_posixpath_new(args[0]);
36+
}
37+
38+
//| def joinpath(self, *args: Union[str, "PosixPath"]) -> "PosixPath":
39+
//| """Join path components.
40+
//|
41+
//| :param args: Path components to join
42+
//| :return: A new PosixPath object
43+
//| """
44+
//| ...
45+
//|
46+
//| def __truediv__(self, other: Union[str, "PosixPath"]) -> "PosixPath":
47+
//| """Implement path / other for joining paths.
48+
//|
49+
//| :param other: Path component to join
50+
//| :return: A new PosixPath object
51+
//| """
52+
//| ...
53+
//|
54+
55+
static mp_obj_t pathlib_posixpath_joinpath(mp_obj_t self_in, mp_obj_t arg) {
56+
pathlib_posixpath_obj_t *self = MP_OBJ_TO_PTR(self_in);
57+
return common_hal_pathlib_posixpath_joinpath(self, arg);
58+
}
59+
MP_DEFINE_CONST_FUN_OBJ_2(pathlib_posixpath_joinpath_obj, pathlib_posixpath_joinpath);
60+
61+
// Binary operator for implementing the / operator
62+
static mp_obj_t pathlib_posixpath_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) {
63+
switch (op) {
64+
case MP_BINARY_OP_TRUE_DIVIDE: {
65+
// Implement path / other
66+
pathlib_posixpath_obj_t *self = MP_OBJ_TO_PTR(lhs);
67+
return common_hal_pathlib_posixpath_joinpath(self, rhs);
68+
}
69+
default:
70+
return MP_OBJ_NULL; // op not supported
71+
}
72+
}
73+
74+
//| @property
75+
//| def parent(self) -> "PosixPath":
76+
//| """The logical parent of the path."""
77+
//| ...
78+
//|
79+
80+
static mp_obj_t pathlib_posixpath_parent(mp_obj_t self_in) {
81+
pathlib_posixpath_obj_t *self = MP_OBJ_TO_PTR(self_in);
82+
return common_hal_pathlib_posixpath_parent(self);
83+
}
84+
MP_DEFINE_CONST_FUN_OBJ_1(pathlib_posixpath_parent_obj, pathlib_posixpath_parent);
85+
86+
MP_PROPERTY_GETTER(pathlib_posixpath_parent_property_obj,
87+
(mp_obj_t)&pathlib_posixpath_parent_obj);
88+
89+
//| @property
90+
//| def name(self) -> str:
91+
//| """The final path component, excluding the drive and root, if any."""
92+
//| ...
93+
//|
94+
95+
static mp_obj_t pathlib_posixpath_name(mp_obj_t self_in) {
96+
pathlib_posixpath_obj_t *self = MP_OBJ_TO_PTR(self_in);
97+
return common_hal_pathlib_posixpath_name(self);
98+
}
99+
MP_DEFINE_CONST_FUN_OBJ_1(pathlib_posixpath_name_obj, pathlib_posixpath_name);
100+
101+
MP_PROPERTY_GETTER(pathlib_posixpath_name_property_obj,
102+
(mp_obj_t)&pathlib_posixpath_name_obj);
103+
104+
//| @property
105+
//| def stem(self) -> str:
106+
//| """The final path component, without its suffix."""
107+
//| ...
108+
//|
109+
110+
static mp_obj_t pathlib_posixpath_stem(mp_obj_t self_in) {
111+
pathlib_posixpath_obj_t *self = MP_OBJ_TO_PTR(self_in);
112+
return common_hal_pathlib_posixpath_stem(self);
113+
}
114+
MP_DEFINE_CONST_FUN_OBJ_1(pathlib_posixpath_stem_obj, pathlib_posixpath_stem);
115+
116+
MP_PROPERTY_GETTER(pathlib_posixpath_stem_property_obj,
117+
(mp_obj_t)&pathlib_posixpath_stem_obj);
118+
119+
//| @property
120+
//| def suffix(self) -> str:
121+
//| """The final component's extension."""
122+
//| ...
123+
//|
124+
125+
static mp_obj_t pathlib_posixpath_suffix(mp_obj_t self_in) {
126+
pathlib_posixpath_obj_t *self = MP_OBJ_TO_PTR(self_in);
127+
return common_hal_pathlib_posixpath_suffix(self);
128+
}
129+
MP_DEFINE_CONST_FUN_OBJ_1(pathlib_posixpath_suffix_obj, pathlib_posixpath_suffix);
130+
131+
MP_PROPERTY_GETTER(pathlib_posixpath_suffix_property_obj,
132+
(mp_obj_t)&pathlib_posixpath_suffix_obj);
133+
134+
//| def exists(self) -> bool:
135+
//| """Check whether the path exists."""
136+
//| ...
137+
//|
138+
139+
static mp_obj_t pathlib_posixpath_exists(mp_obj_t self_in) {
140+
pathlib_posixpath_obj_t *self = MP_OBJ_TO_PTR(self_in);
141+
return common_hal_pathlib_posixpath_exists(self);
142+
}
143+
MP_DEFINE_CONST_FUN_OBJ_1(pathlib_posixpath_exists_obj, pathlib_posixpath_exists);
144+
145+
//| def is_dir(self) -> bool:
146+
//| """Check whether the path is a directory."""
147+
//| ...
148+
//|
149+
150+
static mp_obj_t pathlib_posixpath_is_dir(mp_obj_t self_in) {
151+
pathlib_posixpath_obj_t *self = MP_OBJ_TO_PTR(self_in);
152+
return common_hal_pathlib_posixpath_is_dir(self);
153+
}
154+
MP_DEFINE_CONST_FUN_OBJ_1(pathlib_posixpath_is_dir_obj, pathlib_posixpath_is_dir);
155+
156+
//| def is_file(self) -> bool:
157+
//| """Check whether the path is a regular file."""
158+
//| ...
159+
//|
160+
161+
static mp_obj_t pathlib_posixpath_is_file(mp_obj_t self_in) {
162+
pathlib_posixpath_obj_t *self = MP_OBJ_TO_PTR(self_in);
163+
return common_hal_pathlib_posixpath_is_file(self);
164+
}
165+
MP_DEFINE_CONST_FUN_OBJ_1(pathlib_posixpath_is_file_obj, pathlib_posixpath_is_file);
166+
167+
//| def absolute(self) -> "PosixPath":
168+
//| """Return an absolute version of this path."""
169+
//| ...
170+
//|
171+
172+
static mp_obj_t pathlib_posixpath_absolute(mp_obj_t self_in) {
173+
pathlib_posixpath_obj_t *self = MP_OBJ_TO_PTR(self_in);
174+
return common_hal_pathlib_posixpath_absolute(self);
175+
}
176+
MP_DEFINE_CONST_FUN_OBJ_1(pathlib_posixpath_absolute_obj, pathlib_posixpath_absolute);
177+
178+
//| def resolve(self) -> "PosixPath":
179+
//| """Make the path absolute, resolving any symlinks."""
180+
//| ...
181+
//|
182+
183+
static mp_obj_t pathlib_posixpath_resolve(mp_obj_t self_in) {
184+
pathlib_posixpath_obj_t *self = MP_OBJ_TO_PTR(self_in);
185+
return common_hal_pathlib_posixpath_resolve(self);
186+
}
187+
MP_DEFINE_CONST_FUN_OBJ_1(pathlib_posixpath_resolve_obj, pathlib_posixpath_resolve);
188+
189+
//| def iterdir(self) -> Iterator["PosixPath"]:
190+
//| """Iterate over the files in this directory.
191+
//| Does not include the special paths '.' and '..'.
192+
//|
193+
//| :return: An iterator yielding path objects
194+
//|
195+
//| Example::
196+
//|
197+
//| for path in Path('.').iterdir():
198+
//| print(path)
199+
//| """
200+
//| ...
201+
//|
202+
203+
static mp_obj_t pathlib_posixpath_iterdir(mp_obj_t self_in) {
204+
pathlib_posixpath_obj_t *self = MP_OBJ_TO_PTR(self_in);
205+
return common_hal_pathlib_posixpath_iterdir(self);
206+
}
207+
MP_DEFINE_CONST_FUN_OBJ_1(pathlib_posixpath_iterdir_obj, pathlib_posixpath_iterdir);
208+
209+
static const mp_rom_map_elem_t pathlib_posixpath_locals_dict_table[] = {
210+
{ MP_ROM_QSTR(MP_QSTR_joinpath), MP_ROM_PTR(&pathlib_posixpath_joinpath_obj) },
211+
{ MP_ROM_QSTR(MP_QSTR_exists), MP_ROM_PTR(&pathlib_posixpath_exists_obj) },
212+
{ MP_ROM_QSTR(MP_QSTR_is_dir), MP_ROM_PTR(&pathlib_posixpath_is_dir_obj) },
213+
{ MP_ROM_QSTR(MP_QSTR_is_file), MP_ROM_PTR(&pathlib_posixpath_is_file_obj) },
214+
{ MP_ROM_QSTR(MP_QSTR_absolute), MP_ROM_PTR(&pathlib_posixpath_absolute_obj) },
215+
{ MP_ROM_QSTR(MP_QSTR_resolve), MP_ROM_PTR(&pathlib_posixpath_resolve_obj) },
216+
{ MP_ROM_QSTR(MP_QSTR_iterdir), MP_ROM_PTR(&pathlib_posixpath_iterdir_obj) },
217+
218+
// Properties
219+
{ MP_ROM_QSTR(MP_QSTR_parent), MP_ROM_PTR(&pathlib_posixpath_parent_property_obj) },
220+
{ MP_ROM_QSTR(MP_QSTR_name), MP_ROM_PTR(&pathlib_posixpath_name_property_obj) },
221+
{ MP_ROM_QSTR(MP_QSTR_stem), MP_ROM_PTR(&pathlib_posixpath_stem_property_obj) },
222+
{ MP_ROM_QSTR(MP_QSTR_suffix), MP_ROM_PTR(&pathlib_posixpath_suffix_property_obj) },
223+
};
224+
225+
static MP_DEFINE_CONST_DICT(pathlib_posixpath_locals_dict, pathlib_posixpath_locals_dict_table);
226+
227+
// String representation for PosixPath objects
228+
static void pathlib_posixpath_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
229+
pathlib_posixpath_obj_t *self = MP_OBJ_TO_PTR(self_in);
230+
// Just print the path string directly
231+
mp_obj_print_helper(print, self->path_str, kind);
232+
}
233+
234+
MP_DEFINE_CONST_OBJ_TYPE(
235+
pathlib_posixpath_type,
236+
MP_QSTR_PosixPath,
237+
MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS,
238+
make_new, pathlib_posixpath_make_new,
239+
print, pathlib_posixpath_print,
240+
binary_op, pathlib_posixpath_binary_op,
241+
locals_dict, &pathlib_posixpath_locals_dict
242+
);

shared-bindings/pathlib/PosixPath.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// This file is part of the CircuitPython project: https://circuitpython.org
2+
//
3+
// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries
4+
//
5+
// SPDX-License-Identifier: MIT
6+
7+
#pragma once
8+
9+
#include "py/obj.h"
10+
11+
extern const mp_obj_type_t pathlib_posixpath_type;
12+
13+
// Include the struct definition from shared-module
14+
#include "shared-module/pathlib/PosixPath.h"
15+
16+
mp_obj_t common_hal_pathlib_posixpath_new(mp_obj_t path);
17+
mp_obj_t common_hal_pathlib_posixpath_joinpath(pathlib_posixpath_obj_t *self, mp_obj_t arg);
18+
mp_obj_t common_hal_pathlib_posixpath_parent(pathlib_posixpath_obj_t *self);
19+
mp_obj_t common_hal_pathlib_posixpath_name(pathlib_posixpath_obj_t *self);
20+
mp_obj_t common_hal_pathlib_posixpath_stem(pathlib_posixpath_obj_t *self);
21+
mp_obj_t common_hal_pathlib_posixpath_suffix(pathlib_posixpath_obj_t *self);
22+
mp_obj_t common_hal_pathlib_posixpath_exists(pathlib_posixpath_obj_t *self);
23+
mp_obj_t common_hal_pathlib_posixpath_is_dir(pathlib_posixpath_obj_t *self);
24+
mp_obj_t common_hal_pathlib_posixpath_is_file(pathlib_posixpath_obj_t *self);
25+
mp_obj_t common_hal_pathlib_posixpath_absolute(pathlib_posixpath_obj_t *self);
26+
mp_obj_t common_hal_pathlib_posixpath_resolve(pathlib_posixpath_obj_t *self);
27+
mp_obj_t common_hal_pathlib_posixpath_iterdir(pathlib_posixpath_obj_t *self);

0 commit comments

Comments
 (0)