Repository navigation
Expand file tree
/
Copy pathpyproject.toml
More file actions
165 lines (151 loc) · 6.46 KB
/
Copy pathpyproject.toml
File metadata and controls
165 lines (151 loc) · 6.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# SPDX-License-Identifier: MIT
# Copyright (c) 2026 The Pybricks Authors
#
# Virtual workspace root: intentionally has no [project] table, so there is no
# root package to build or publish. Use `uv sync --all-packages` to install
# every member editable.
[tool.uv.workspace]
members = ["packages/*"]
[dependency-groups]
lint = ["ruff>=0.16.5"]
typecheck = ["pyright>=1.1.400"]
test = [
"pytest>=8.3",
"pytest-timeout>=2.3",
"coverage[toml]>=7.6",
"anyio>=4.14",
]
docs = [
# ProperDocs is the build tool, not MkDocs. MkDocs 2.0 removes the plugin
# and theming systems with no migration path; ProperDocs continues the 1.6
# line and reads both the mkdocs.* and properdocs.* entry point groups, so
# Material, mkdocstrings, gen-files and literate-nav all load unchanged.
"properdocs>=1.6.7",
# Still required, as a library rather than as the build tool: Material and
# mkdocstrings import mkdocs at runtime. Pinned below 2.0 for the reason
# above; mkdocs-material pins it there too.
"mkdocs>=1.6,<2.0",
"mkdocs-material>=9.5",
"mkdocstrings[python]>=0.27",
"mkdocs-gen-files>=0.5",
"mkdocs-literate-nav>=0.6",
]
dev = [
{ include-group = "lint" },
{ include-group = "typecheck" },
{ include-group = "test" },
{ include-group = "docs" },
]
# ---------------------------------------------------------------- ruff
[tool.ruff]
line-length = 88
target-version = "py311"
# examples/ is MicroPython that runs on a hub, not code that runs here: the
# pybricks modules it imports do not exist in this environment, and its style
# is whatever reads best for someone learning from it.
extend-exclude = [".venv", "examples"]
[tool.ruff.lint]
select = [
"E", "W", # pycodestyle
"F", # pyflakes
"I", # isort
"UP", # pyupgrade
"B", # flake8-bugbear
"SIM", # flake8-simplify
"C4", # flake8-comprehensions
"PTH", # flake8-use-pathlib
"RUF", # ruff-specific
"TID", # flake8-tidy-imports
"ASYNC", # flake8-async -- enforces the structured concurrency rules
"LOG", # flake8-logging
"G", # flake8-logging-format
"BLE", # flake8-blind-except -- enforces "never swallow cancellation"
"D", # pydocstyle
]
ignore = [
"E501", # line length is the formatter's job
"D203", "D213", # mutually exclusive with the google convention pair
"D202", # a block after a docstring gets its blank line, as
# every block does. See CONTRIBUTING.md.
]
[tool.ruff.lint.pydocstyle]
convention = "google"
[tool.ruff.lint.isort]
known-first-party = ["pb_cli", "pb_client", "pb_core", "pb_service"]
# Structured concurrency, enforced mechanically. See CONTRIBUTING.md.
[tool.ruff.lint.flake8-tidy-imports.banned-api]
"asyncio.create_task".msg = "Use an anyio task group (structured concurrency rule)."
"asyncio.ensure_future".msg = "Use an anyio task group (structured concurrency rule)."
"asyncio.gather".msg = "Use an anyio task group."
"asyncio.wait".msg = "Use an anyio task group plus a cancel scope."
"asyncio.set_event_loop_policy".msg = "Never change the loop policy; Windows named pipes require the Proactor loop."
[tool.ruff.lint.per-file-ignores]
# Docstrings are required on the library surface, not on CLI/service internals.
"packages/pb-cli/src/**" = ["D100", "D101", "D102", "D103", "D104", "D105", "D107"]
"packages/pb-service/src/**" = ["D100", "D101", "D102", "D103", "D104", "D105", "D107"]
"**/tests/**" = ["D"]
"docs/**" = ["D"]
[tool.ruff.format]
docstring-code-format = true
# ---------------------------------------------------------------- pytest
[tool.pytest.ini_options]
testpaths = ["packages", "tests"]
# importlib mode: lets each package have its own tests/test_smoke.py without
# __init__.py files or globally unique basenames. Correct default for src layout.
addopts = "-ra --strict-markers --strict-config --import-mode=importlib"
timeout = 30
markers = [
"hardware: requires a real Pybricks hub (deselected in CI)",
"console: requires a real OS console (Win32 console or POSIX pty)",
]
filterwarnings = ["error"]
# ---------------------------------------------------------------- coverage
[tool.coverage.run]
branch = true
source = ["pb_cli", "pb_client", "pb_core", "pb_service"]
parallel = true
[tool.coverage.paths]
# One package's file has a different absolute path on every runner, so without
# this a combined report lists the Windows copy and the POSIX copy as two
# files and credits neither with what the other covered. The first entry is
# what they are all reported as.
source = ["packages/", "*/packages/"]
[tool.coverage.report]
show_missing = true
# The floor for a report from one machine, which is a rough guide rather than
# the gate. One runner on its own reads several points lower, because the other
# platforms' backends -- the AF_UNIX listener, the named pipe, the two ways of
# reading a keystroke -- count as missed there and are only credited once the
# runs are put together. The gate is the coverage job in CI, which combines
# every runner's data and asks for 85.
fail_under = 80
exclude_also = [
"if TYPE_CHECKING:",
"raise NotImplementedError",
"@overload",
"if sys.platform ==",
]
# ---------------------------------------------------------------- pyright
[tool.pyright]
# "**" rather than "*", and the difference is the whole gate. A "*" path
# segment matches nothing here -- measured against pyright 1.1.411 by planting
# a type error in packages/pb-cli/src and watching it go unreported, while the
# same error in tests/ was caught -- so this read as the whole repository and
# checked seven files.
include = ["packages/**/src", "packages/**/tests", "tests", "tools"]
# "ignore" rather than "exclude": an excluded file still gets red squiggles in
# the editor when it is open, and examples/ is MicroPython for a hub, whose
# imports cannot resolve here and never will.
ignore = ["examples"]
typeCheckingMode = "strict"
pythonVersion = "3.11"
# Every platform at once, rather than whichever one is running. Half of this
# repository is per-platform backends, and on the default setting the other
# half of every one of them is skipped as unreachable -- so a mistake in the
# POSIX console could only be found by a POSIX runner. Measured: a planted
# POSIX-only error is invisible on Windows by default and reported under
# "All". It also means the three CI runners agree about what is wrong.
pythonPlatform = "All"
venvPath = "."
venv = ".venv"
reportMissingTypeStubs = false