Skip to content

Commit 71823bf

Browse files
committed
use basedpyright instead of mypy for typechecking
1 parent 56dbddf commit 71823bf

13 files changed

Lines changed: 472 additions & 434 deletions

File tree

.github/workflows/linting.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,17 @@ jobs:
1515
- name: Install uv
1616
uses: astral-sh/setup-uv@v7
1717
- name: Set up Python
18-
run: uv python install
18+
run: uv python install && uv venv
1919
- name: Ruff lint
2020
run: |
2121
uvx ruff check
2222
- name: Vulture
2323
if: success() || failure()
2424
run: |
2525
uvx vulture ./src --min-confidence 61
26+
- name: basedpyright
27+
if: success() || failure()
28+
run: uvx basedpyright --warnings
2629
- name: Ruff format
2730
if: success() || failure()
2831
run: |

docs/assets/image_gen.py

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from unittest.mock import MagicMock
99

1010
import build123d
11-
from build123d import Box, Part
11+
from build123d import Box, Compound, Part
1212

1313
from gridfinity_build123d import (
1414
Base,
@@ -50,17 +50,16 @@ class CameraPosition(Enum):
5050

5151
@staticmethod
5252
def pos_to_str(pos: CameraPosition) -> str:
53-
if pos == CameraPosition.CAMERA_TOP:
54-
return "0,0,0,55,0,25,0"
55-
if pos == CameraPosition.CAMERA_BOT:
56-
return "0,0,0,125,0,25,0"
57-
msg = "Unknown camera position"
58-
raise ValueError(msg)
53+
match pos:
54+
case CameraPosition.CAMERA_TOP:
55+
return "0,0,0,55,0,25,0"
56+
case CameraPosition.CAMERA_BOT:
57+
return "0,0,0,125,0,25,0"
5958

6059

6160
class Convert:
6261
@staticmethod
63-
def part_to_png(part: Part, file_name: str, camera_pos: CameraPosition) -> None:
62+
def part_to_png(part: Compound, file_name: str, camera_pos: CameraPosition) -> None:
6463
with TemporaryDirectory() as tmp_dir:
6564
Convert._part_to_png(part, Path(tmp_dir), file_name, camera_pos)
6665

@@ -78,7 +77,7 @@ def parts_to_gif(
7877
str(Path(tmp_dir).joinpath(f"{idx}".zfill(3))),
7978
camera_pos,
8079
)
81-
check_call(
80+
_ = check_call(
8281
[
8382
"/usr/bin/convert",
8483
"-delay",
@@ -94,18 +93,18 @@ def parts_to_gif(
9493

9594
@staticmethod
9695
def _part_to_png(
97-
part: Part,
96+
part: Compound,
9897
work_dir: Path,
9998
file_name: str,
10099
camera_pos: CameraPosition,
101100
) -> None:
102101
tmp_stl = work_dir.joinpath("tmp.stl")
103102
tmp_scad = work_dir.joinpath("tmp.scad")
104-
build123d.export_stl(part, str(tmp_stl))
103+
_ = build123d.export_stl(part, str(tmp_stl)) # pyright: ignore[reportUnknownMemberType]
105104
with tmp_scad.open("w") as file:
106-
file.write(f'import("{tmp_stl}");\n')
105+
_ = file.write(f'import("{tmp_stl}");\n')
107106

108-
check_call(
107+
_ = check_call(
109108
[
110109
"/usr/bin/openscad",
111110
"--autocenter",

pyproject.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ requires-python = ">=3.10"
1010

1111
[dependency-groups]
1212
dev = [
13-
"mypy",
1413
"coverage",
1514
"vulture",
1615
"ocp_vscode",
@@ -21,6 +20,7 @@ dev = [
2120
"sphinx-rtd-theme",
2221
"sphinx_design",
2322
"debugpy>=1.8.20",
23+
"basedpyright>=1.38.2",
2424
]
2525

2626
[build-system]
@@ -63,3 +63,6 @@ ignore-decorators = ['inherit_docstring']
6363

6464
[tool.ruff.lint.pycodestyle]
6565
max-line-length = 101
66+
67+
[tool.basedpyright]
68+
ignore = ["tests"]

src/gridfinity_build123d/base.py

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
Mode,
1616
Rectangle,
1717
RotationLike,
18-
add,
18+
add, # pyright: ignore[reportUnknownVariableType]
1919
extrude,
2020
)
2121

@@ -60,7 +60,7 @@ def __init__(
6060

6161
with BuildPart() as base:
6262
base_block = BaseBlock(features=features, mode=Mode.PRIVATE)
63-
Utils.place_by_grid(
63+
_ = Utils.place_by_grid(
6464
base_block,
6565
grid,
6666
width=gridfinity_standard.grid.size,
@@ -71,7 +71,7 @@ def __init__(
7171
z_top = top_face_1.bounding_box().min.Z
7272

7373
with Locations((0, 0, z_top)):
74-
Utils.create_bin_platform(
74+
_ = Utils.create_bin_platform(
7575
grid,
7676
align=(
7777
Align.CENTER,
@@ -80,6 +80,10 @@ def __init__(
8080
),
8181
)
8282

83+
if not base.part:
84+
msg = "Base is empty"
85+
raise RuntimeError(msg)
86+
8387
super().__init__(base.part, rotation, align, mode)
8488

8589

@@ -107,7 +111,7 @@ def __init__(
107111
object. Defaults to None.
108112
mode (Mode, optional): Combination mode. Defaults to Mode.ADD.
109113
"""
110-
grid = []
114+
grid: list[list[bool]] = []
111115
for _ in range(grid_y):
112116
grid += [[True] * grid_x]
113117
super().__init__(grid, features, rotation, align, mode)
@@ -142,14 +146,18 @@ def __init__(
142146
features = features if isinstance(features, Iterable) else [features]
143147

144148
with BuildPart() as baseblock:
145-
Utils.create_profile_block(
149+
_ = Utils.create_profile_block(
146150
StackProfile.ProfileType.BIN,
147151
gridfinity_standard.stacking_lip.offset,
148152
)
149153

150154
for feature in features:
151155
feature.apply(baseblock)
152156

157+
if not baseblock.part:
158+
msg = "Part is empty"
159+
raise RuntimeError(msg)
160+
153161
super().__init__(baseblock.part, rotation, align, mode)
154162

155163

@@ -186,16 +194,20 @@ def __init__(
186194
base_block = BaseBlock(features=[], rotation=rotation, mode=Mode.ADD)
187195

188196
with BuildPart() as baseblock_platform:
189-
add(base_block)
197+
_ = add(base_block)
190198

191199
with BuildSketch(baseblock_platform.faces().sort_by(Axis.Z)[-1]) as rect2:
192-
Rectangle(gridfinity_standard.grid.size, gridfinity_standard.grid.size)
193-
extrude(
200+
_ = Rectangle(gridfinity_standard.grid.size, gridfinity_standard.grid.size)
201+
_ = extrude(
194202
to_extrude=rect2.sketch,
195203
amount=gridfinity_standard.bottom.platform_height,
196204
)
197205

198206
for feature in features:
199207
feature.apply(baseblock_platform)
200208

209+
if not baseblock_platform.part:
210+
msg = "Part is empty"
211+
raise RuntimeError(msg)
212+
201213
super().__init__(baseblock_platform.part, rotation, align, mode)

src/gridfinity_build123d/baseplate.py

Lines changed: 61 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55

66
from __future__ import annotations
77

8+
from abc import ABC
89
from collections.abc import Iterable
910
from math import isclose
10-
from typing import TYPE_CHECKING
11+
from typing import TYPE_CHECKING, override
1112

1213
from build123d import (
1314
Align,
@@ -17,11 +18,13 @@
1718
BuildLine,
1819
BuildPart,
1920
BuildSketch,
21+
Edge,
2022
Line,
2123
Mode,
2224
Plane,
2325
RotationLike,
24-
add,
26+
Shape,
27+
add, # pyright: ignore[reportUnknownVariableType]
2528
extrude,
2629
fillet,
2730
make_face,
@@ -34,7 +37,7 @@
3437
from gridfinity_build123d.features import Feature
3538

3639

37-
class BasePlateBlock(ObjectCreate):
40+
class BasePlateBlock(ObjectCreate, ABC):
3841
"""Single base plate block used to construct a bigger baseplate."""
3942

4043
def __init__(
@@ -50,7 +53,7 @@ def __init__(
5053
if not features:
5154
features = []
5255

53-
self.features = features if isinstance(features, Iterable) else [features]
56+
self.features: list[Feature] = features if isinstance(features, Iterable) else [features]
5457

5558

5659
class BasePlateBlockFrame(BasePlateBlock):
@@ -61,6 +64,7 @@ class BasePlateBlockFrame(BasePlateBlock):
6164
features. Defaults to None.
6265
"""
6366

67+
@override
6468
def create_obj(
6569
self,
6670
rotation: RotationLike = (0, 0, 0),
@@ -69,20 +73,28 @@ def create_obj(
6973
) -> BasePartObject:
7074
"""Overwrites BasePlateBlock.create_obj."""
7175
with BuildPart() as block:
72-
Utils.create_profile_block(StackProfile.ProfileType.PLATE)
76+
_ = Utils.create_profile_block(StackProfile.ProfileType.PLATE)
77+
78+
if not block.part:
79+
msg = "block is empty"
80+
raise RuntimeError(msg)
7381

7482
with BuildPart() as part:
75-
Box(
83+
_ = Box(
7684
42,
7785
42,
7886
block.part.bounding_box().size.Z,
7987
align=(Align.CENTER, Align.CENTER, Align.MIN),
8088
)
81-
add(block.part, mode=Mode.SUBTRACT)
89+
_ = add(block.part, mode=Mode.SUBTRACT)
8290

8391
for feature in self.features:
8492
feature.apply(part)
8593

94+
if not part.part:
95+
msg = "Part is empty"
96+
raise RuntimeError(msg)
97+
8698
return BasePartObject(part.part, rotation, align, mode)
8799

88100

@@ -102,9 +114,10 @@ def __init__(
102114
features. Defaults to None.
103115
"""
104116
super().__init__(features)
105-
self.bottom_height = bottom_height
117+
self.bottom_height: float = bottom_height
106118

107-
def create_obj( # noqa: D102
119+
@override
120+
def create_obj(
108121
self,
109122
rotation: RotationLike = (0, 0, 0),
110123
align: Align | tuple[Align, Align, Align] | None = None,
@@ -114,21 +127,26 @@ def create_obj( # noqa: D102
114127
frame = BasePlateBlockFrame().create_obj(mode=Mode.PRIVATE)
115128
with BuildSketch():
116129
bot_face = frame.faces().sort_by(Axis.Z)[0]
117-
make_face(bot_face.outer_wire())
118-
extrude(amount=self.bottom_height, dir=(0, 0, -1))
130+
_ = make_face(bot_face.outer_wire().edges())
131+
_ = extrude(amount=self.bottom_height, dir=(0, 0, -1))
119132

120133
for feature in self.features:
121134
feature.apply(part)
122135

123-
add(frame)
136+
_ = add(frame)
137+
138+
if not part.part:
139+
msg = "Part is empty"
140+
raise RuntimeError(msg)
124141

125142
return BasePartObject(part.part, rotation, align, mode)
126143

127144

128145
class BasePlateBlockSkeleton(BasePlateBlockFull):
129146
"""Placeholder for future skeletonized baseplate."""
130147

131-
def create_obj( # noqa: D102
148+
@override
149+
def create_obj(
132150
self,
133151
rotation: RotationLike = (0, 0, 0),
134152
align: Align | tuple[Align, Align, Align] | None = None,
@@ -141,20 +159,25 @@ def create_obj( # noqa: D102
141159
length_l = length / 2
142160

143161
with BuildPart() as part:
144-
super().create_obj()
162+
_ = super().create_obj()
145163
with BuildSketch():
146164
with BuildLine() as line:
147165
ln1 = Line((0, length_l), (length_s, length_l))
148166
ln2 = Line(ln1 @ 1, (length_s, length_s))
149167
ln3 = Line(ln2 @ 1, (length_l, length_s))
150-
Line(ln3 @ 1, (length_l, 0))
151-
vertex = line.vertices().sort_by_distance((length / 4, length / 4))[0]
152-
fillet(vertex, radius)
153-
mirror(about=Plane.XZ)
154-
mirror(about=Plane.YZ)
155-
156-
make_face()
157-
extrude(amount=-self.bottom_height, mode=Mode.SUBTRACT)
168+
_ = Line(ln3 @ 1, (length_l, 0))
169+
vertex = line.vertices().sort_by_distance((length / 4, length / 4))[0] # pyright: ignore[reportUnknownMemberType]
170+
_ = fillet(vertex, radius)
171+
_ = mirror(about=Plane.XZ)
172+
_ = mirror(about=Plane.YZ)
173+
174+
_ = make_face()
175+
_ = extrude(amount=-self.bottom_height, mode=Mode.SUBTRACT)
176+
177+
if not part.part:
178+
msg = "Part is empty"
179+
raise RuntimeError(msg)
180+
158181
return BasePartObject(part.part, rotation, align, mode)
159182

160183

@@ -190,19 +213,27 @@ def __init__(
190213
if not features:
191214
features = []
192215

193-
self.features = features if isinstance(features, Iterable) else [features]
216+
self.features: list[Feature] = features if isinstance(features, Iterable) else [features]
194217

195218
with BuildPart() as part:
196-
Utils.place_by_grid(baseplate_block.create_obj(mode=Mode.PRIVATE), grid)
219+
_ = Utils.place_by_grid(baseplate_block.create_obj(mode=Mode.PRIVATE), grid)
220+
221+
if not part.part:
222+
msg = "Part is empty"
223+
raise RuntimeError(msg)
197224

198225
z_height = part.part.bounding_box().size.Z
199226

200-
wires = (
201-
part.edges()
202-
.filter_by(Axis.Z)
203-
.filter_by(lambda edge: isclose(edge.length, z_height))
204-
)
205-
fillet(wires, 4)
227+
def edge_filter(shape: Shape[Edge]) -> bool:
228+
inner_edge = shape.edge()
229+
if not inner_edge:
230+
msg = "Edge is empty"
231+
raise RuntimeError(msg)
232+
233+
return isclose(inner_edge.length, z_height)
234+
235+
wires = part.edges().filter_by(Axis.Z).filter_by(edge_filter)
236+
_ = fillet(wires, 4)
206237

207238
for feature in self.features:
208239
feature.apply(part)

0 commit comments

Comments
 (0)