Skip to content

Commit c5bab4c

Browse files
committed
pybricksdev.compile: fix multi-file compile of __init__.py files
Add additional searches for package __init__.py files when compiling multi-file projects. This was missed in the original implementation of import lookup using mpy-tool. Fixes: #131
1 parent b7acc8b commit c5bab4c

File tree

3 files changed

+44
-3
lines changed

3 files changed

+44
-3
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88

99
### Fixed
1010
- Removed debug print statements in `pybricksdev.compile.compile_multi_file()`.
11+
- Fixed `__init__.py` files in packages not being included when compiling multi-file projects ([pybricksdev#131]).
12+
13+
[pybricksdev#131]: https://github.com/pybricks/pybricksdev/issues/131
1114

1215
## [2.3.1] - 2026-01-18
1316

pybricksdev/compile.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,10 +180,20 @@ async def _compile_module_and_get_imports(
180180
"""
181181
with TemporaryDirectory() as temp_dir:
182182
module_path = os.path.join(*module_name.split(".")) + ".py"
183+
package_path = os.path.join(*module_name.split("."), "__init__.py")
183184

184185
# TODO: check for pre-compiled .mpy file first?
185186

186-
mpy = await compile_file(proj_dir, module_path, abi)
187+
if os.path.exists(os.path.join(proj_dir, module_path)):
188+
src_path = module_path
189+
elif os.path.exists(os.path.join(proj_dir, package_path)):
190+
src_path = package_path
191+
else:
192+
raise FileNotFoundError(
193+
f"Module {module_name} not found. Searched for {module_path} and {package_path}."
194+
)
195+
196+
mpy = await compile_file(proj_dir, src_path, abi)
187197

188198
mpy_path = os.path.join(temp_dir, TMP_MPY_SCRIPT)
189199

tests/test_compile.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ async def test_compile_multi_file(abi: int):
7474
"import test1\n",
7575
"from test2 import thing2\n",
7676
"from nested.test3 import thing3\n",
77+
"from test4 import thing4\n",
78+
"from nested.test5 import thing5\n",
7779
]
7880
)
7981

@@ -101,6 +103,24 @@ async def test_compile_multi_file(abi: int):
101103
) as f3:
102104
f3.write("thing3 = 'thing3'\n")
103105

106+
# test4 and test5 are to test package modules with non-empty __init__.py
107+
108+
os.mkdir("test4")
109+
110+
with open(
111+
os.path.join(temp_dir, "test4", "__init__.py"), "w", encoding="utf-8"
112+
) as f4:
113+
f4.write("thing4 = 'thing4'\n")
114+
115+
os.mkdir(os.path.join("nested", "test5"))
116+
117+
with open(
118+
os.path.join(temp_dir, "nested", "test5", "__init__.py"),
119+
"w",
120+
encoding="utf-8",
121+
) as f5:
122+
f5.write("thing5 = 'thing5'\n")
123+
104124
multi_mpy = await compile_multi_file("test.py", abi)
105125
pos = 0
106126

@@ -140,6 +160,12 @@ def unpack_mpy(data: bytes) -> tuple[bytes, bytes]:
140160
name5, mpy5 = unpack_mpy(multi_mpy)
141161
names.add(name5.decode())
142162

163+
name6, mpy6 = unpack_mpy(multi_mpy)
164+
names.add(name6.decode())
165+
166+
name7, mpy7 = unpack_mpy(multi_mpy)
167+
names.add(name7.decode())
168+
143169
assert pos == len(multi_mpy)
144170

145171
# It is important that the main module is first.
@@ -153,9 +179,9 @@ def unpack_mpy(data: bytes) -> tuple[bytes, bytes]:
153179
assert "nested.test3" in names
154180

155181
if uses_module_finder:
156-
assert len(names) == 4
182+
assert len(names) == 6
157183
else:
158-
assert len(names) == 3
184+
assert len(names) == 5
159185

160186
def check_mpy(mpy: bytes) -> None:
161187
magic, abi_ver, flags, int_bits = struct.unpack_from("<BBBB", mpy)
@@ -171,3 +197,5 @@ def check_mpy(mpy: bytes) -> None:
171197
if uses_module_finder:
172198
check_mpy(mpy4) # pyright: ignore[reportPossiblyUnboundVariable]
173199
check_mpy(mpy5)
200+
check_mpy(mpy6)
201+
check_mpy(mpy7)

0 commit comments

Comments
 (0)