diff --git a/README.md b/README.md index 74ae4f2c..d738e53a 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,18 @@ Median cold full-file check across 26 sin +## Install + +The CLI is on [PyPI as `basilisk-python`](https://pypi.org/project/basilisk-python/) — install it as a standalone tool; the installed command is `basilisk`: + +```sh +uv tool install basilisk-python # or: pipx install basilisk-python +``` + +Also available via Homebrew (`brew tap Nimblesite/tap && brew install basilisk`), Scoop, and +[GitHub Releases](https://github.com/Nimblesite/Basilisk/releases) — every channel ships the same +single Rust binary. Full options: [install guide](https://www.basilisk-python.dev/docs/install-cli/). + ## Try it The `examples/` folder has ready-to-go Python files: diff --git a/README.zh.md b/README.zh.md index 92660e13..7f5dabae 100644 --- a/README.zh.md +++ b/README.zh.md @@ -52,6 +52,18 @@ Basilisk 是**唯一**在官方 在 Apple M4 Max 上,跨 26 个单一类型构造压力测试样本的冷启动全文件检查中位数 —— 数值越低越好。Basilisk 的热重检查可降至约 4 ms。每个数字均由 [`hyperfine`](https://github.com/sharkdp/hyperfine) 生成并按机器提交,没有一个是手写的。**克隆仓库,在你自己的硬件上运行 `make bench`,并把 CSV 发给我们 —— 欢迎社区独立复核。** [完整基准测试与方法论(英文)→](https://www.basilisk-python.dev/docs/benchmarks/) +## 安装 + +CLI 已发布到 [PyPI,包名为 `basilisk-python`](https://pypi.org/project/basilisk-python/)——请将其作为独立工具安装,安装后的命令是 `basilisk`: + +```sh +uv tool install basilisk-python # 或:pipx install basilisk-python +``` + +也可通过 Homebrew(`brew tap Nimblesite/tap && brew install basilisk`)、Scoop 以及 +[GitHub Releases](https://github.com/Nimblesite/Basilisk/releases) 安装——所有渠道分发的都是同一个 +Rust 二进制文件。完整选项参见[安装指南](https://www.basilisk-python.dev/docs/install-cli/)。 + ## 试用 `examples/` 文件夹中提供了可直接运行的 Python 文件: diff --git a/docs/specs/LSP-CONFIGURATION-EDITOR-SPEC.md b/docs/specs/LSP-CONFIGURATION-EDITOR-SPEC.md index 1e903499..e93b1e5c 100644 --- a/docs/specs/LSP-CONFIGURATION-EDITOR-SPEC.md +++ b/docs/specs/LSP-CONFIGURATION-EDITOR-SPEC.md @@ -149,8 +149,13 @@ from [STUBRES-TYPESHED-WARN](CHECKER-STUB-RESOLUTION-SPEC.md#STUBRES-TYPESHED-WA | verification waived | persistent `UNVERIFIED` | | custom | persistent `USER-MANAGED SOURCE` | -Rows may coexist, never poll, and carry no command; fixes remain in the editable -section. They describe Basilisk transport around pinned typing step 3, not extra +Rows may coexist, never poll, and never mutate; fixes remain in the editable +section. A warning row's message names its fix (e.g. `UNPINNED`'s **Pin +current**), so the row carries exactly one navigation-only command that opens +the configuration editor — attached only while the server advertises the +editor capability, because the open command is capability-gated and a +shown-but-dead command is forbidden. Other rows carry no command. They +describe Basilisk transport around pinned typing step 3, not extra typing diagnostics ([`python/typing@6ef9f77`](https://github.com/python/typing/blob/6ef9f7719ecfff09dad8724ef42b621fd994fb5e/docs/spec/distributing.rst)). diff --git a/vscode-extension/src/info-panel.ts b/vscode-extension/src/info-panel.ts index abe0ebd4..fc49ada4 100644 --- a/vscode-extension/src/info-panel.ts +++ b/vscode-extension/src/info-panel.ts @@ -18,6 +18,7 @@ import { effect } from "@preact/signals-core"; import type { LanguageClient } from "vscode-languageclient/node"; import { type Store } from "./store"; import type { TypeshedStatusState } from "./configuration-editor-model"; +import { CONFIGURATION_EDITOR_COMMAND, supportsConfigurationEditor } from "./configuration-editor"; // ── Tree node types ────────────────────────────────────────────────────── @@ -264,8 +265,38 @@ function typeshedSourceItem( return item; } +// Implements the navigation affordance of [LSPCFGED-TYPESHED-SERVICE-INFO]: +// warning rows never mutate (fixes stay in the editable section), but their +// messages name actions that live in the configuration editor (e.g. the +// UNPINNED row's "Pin current"), so a warning row navigates there on click. +// The command is attached only while the server advertises the editor — +// basilisk.openConfigurationEditor is capability-gated +// (configuration-editor-registration.ts), and a shown-but-dead command is +// exactly issue #103 defect 1. +function typeshedWarningItem( + prefix: string, + warning: TypeshedStatusState["warnings"][number], + editorSupported: boolean, +): InfoTextItem { + const item = new InfoTextItem( + `${prefix} ${warning.code}`, + warning.message, + statusKind(warning.severity) === "High" ? "warning" : "info", + ); + if (editorSupported) { + item.contextValue = "typeshed-warning"; + item.tooltip = "Click to open the Configuration Editor, where the typeshed fixes live"; + item.command = { + command: CONFIGURATION_EDITOR_COMMAND, + title: "Open Configuration Editor", + }; + } + return item; +} + function typeshedInfoItems( statuses: ReadonlyMap, + editorSupported: boolean, ): InfoTextItem[] { const entries = [...statuses.entries()].sort(([left], [right]) => left.localeCompare(right)); return entries.flatMap(([rootUri, status]) => { @@ -280,11 +311,7 @@ function typeshedInfoItems( ...(status.blockedReason === undefined ? [] : [new InfoTextItem(`${prefix} Blocked`, status.blockedReason, "error")]), - ...status.warnings.map((warning) => new InfoTextItem( - `${prefix} ${warning.code}`, - warning.message, - statusKind(warning.severity) === "High" ? "warning" : "info", - )), + ...status.warnings.map((warning) => typeshedWarningItem(prefix, warning, editorSupported)), ]; return rows; }); @@ -377,7 +404,7 @@ export class InfoPanelProvider implements vscode.TreeDataProvider, vsc ...(binary !== undefined && binary !== null ? [new InfoTextItem("Binary", formatResolvedTool(binary), "file-binary")] : []), - ...typeshedInfoItems(this.store.typeshedStatuses.value), + ...typeshedInfoItems(this.store.typeshedStatuses.value, supportsConfigurationEditor(client)), ]; return new SectionItem("Server Info", items); diff --git a/vscode-extension/src/test/suite/info-panel.test.ts b/vscode-extension/src/test/suite/info-panel.test.ts index fbdbbf27..f98e3c06 100644 --- a/vscode-extension/src/test/suite/info-panel.test.ts +++ b/vscode-extension/src/test/suite/info-panel.test.ts @@ -134,6 +134,90 @@ function verifyAcquiringTypeshedSpinner(): void { } } +/** A store whose single root reports the UNPINNED typeshed warning. */ +function storeWithUnpinnedWarning(): ReturnType { + const store = createStore(); + const writable = store.typeshedStatuses as unknown as { + value: ReadonlyMap; + }; + writable.value = new Map([[ + "file:///workspace", + { + lifecycle: { kind: "Ready" }, activeSource: { kind: "Latest" }, + blockedReason: undefined, + commitIdentity: "6fb14c98ee340a07eea807a4c804e20a849eb92b", + treeIdentity: undefined, + transport: { kind: "Codeload" }, licenseStatus: { kind: "Approved" }, + licenseReference: "typeshed://license/6fb14c9", provenance: { kind: "GithubTlsAttested" }, + signedRelease: false, + warnings: [{ + code: "UNPINNED", message: "Pin current to make this reproducible", + severity: { kind: "Advisory" }, + }], + }, + ]]); + return store; +} + +// Tests [LSPCFGED-TYPESHED-SERVICE-INFO] navigation + [EXTACT-INFO-AFFORDANCE]: +// the UNPINNED row's own message tells the user to "Pin current", and the Pin +// current action lives in the configuration editor — so the row must navigate +// there when the editor capability is live (info-panel.ts typeshedInfoItems). +function verifyUnpinnedWarningRowOpensConfigurationEditor(): void { + const store = storeWithUnpinnedWarning(); + const writableClient = store.client as unknown as { value: unknown }; + writableClient.value = { + initializeResult: { + capabilities: { experimental: { basilisk: { configurationEditor: true } } }, + }, + }; + const typeshedProvider = new InfoPanelProvider(store); + try { + const section = typeshedProvider.getChildren().find((row) => labelOf(row) === "Server Info"); + assert.ok(section, "Server Info section should exist"); + const unpinned = typeshedProvider + .getChildren(section) + .find((row) => labelOf(row) === "Typeshed UNPINNED"); + assert.ok(unpinned, "the UNPINNED warning row should exist"); + assert.strictEqual( + unpinned.command?.command, + "basilisk.openConfigurationEditor", + "the UNPINNED row advertises Pin current, so clicking it must open the configuration editor where Pin current lives", + ); + const tip = tooltipOf(unpinned).trim(); + assert.ok( + tip.length > 0, + "an actionable row must carry an imperative tooltip describing its effect", + ); + } finally { + typeshedProvider.dispose(); + } +} + +// Regression guard for issue #103 defect 1: basilisk.openConfigurationEditor +// is capability-gated (configuration-editor-registration.ts), so a warning row +// must NOT carry it while no server advertises the editor — a shown-but-dead +// command raises "command not found". +function verifyUnpinnedWarningRowStaysInertWithoutEditorCapability(): void { + const store = storeWithUnpinnedWarning(); + const typeshedProvider = new InfoPanelProvider(store); + try { + const section = typeshedProvider.getChildren().find((row) => labelOf(row) === "Server Info"); + assert.ok(section, "Server Info section should exist"); + const unpinned = typeshedProvider + .getChildren(section) + .find((row) => labelOf(row) === "Typeshed UNPINNED"); + assert.ok(unpinned, "the UNPINNED warning row should exist"); + assert.strictEqual( + unpinned.command, + undefined, + "without the configuration-editor capability the row must not carry a dead command", + ); + } finally { + typeshedProvider.dispose(); + } +} + suite("Basilisk Info Panel Contents (slimmed, issue #103)", () => { let provider: InfoPanelProvider; @@ -219,6 +303,16 @@ suite("Basilisk Info Panel Contents (slimmed, issue #103)", () => { test("Server Info shows an acquiring Typeshed spinner", verifyAcquiringTypeshedSpinner); + test( + "the UNPINNED warning row opens the configuration editor where Pin current lives", + verifyUnpinnedWarningRowOpensConfigurationEditor, + ); + + test( + "the UNPINNED warning row stays inert while no server advertises the configuration editor", + verifyUnpinnedWarningRowStaysInertWithoutEditorCapability, + ); + // Tests [EXTACT-INFO-SERVER-INFO]: one uv row, sub-settings in the tooltip. test("uv sub-settings are folded into the uv row tooltip, not separate rows", () => { const rows = serverInfoRows(); diff --git a/website/src/docs/configuration.md b/website/src/docs/configuration.md index 3c11d5ef..be5a3db3 100644 --- a/website/src/docs/configuration.md +++ b/website/src/docs/configuration.md @@ -1,10 +1,10 @@ --- layout: layouts/docs.njk title: "Configuration Reference — pyproject.toml Settings" -description: "Complete reference for all Basilisk configuration options in pyproject.toml. Severity overrides, per-path rules, inline suppressions, and Ruff integration." +description: "Complete reference for all Basilisk configuration options in pyproject.toml. Rule and tag severities, typeshed source pinning, inline suppressions, and folder-scoped configuration." keywords: basilisk, configuration, pyproject.toml, settings date: 2026-02-28 -dateModified: 2026-07-14 +dateModified: 2026-07-19 author: The Basilisk Project eleventyNavigation: key: Configuration @@ -14,11 +14,19 @@ eleventyNavigation: # Configuration Reference `[tool.basilisk]` in `pyproject.toml` is the only configuration source. For -each file it checks, Basilisk walks up from the file's directory and reads -every ancestor `pyproject.toml` that carries a `[tool.basilisk]` table. The -tables merge cumulatively, with the nearest file winning wherever the same key -is set — a `pyproject.toml` in a child folder refines the root configuration, -it never replaces it. +each file it checks, Basilisk walks up from the file's directory and visits +every ancestor `pyproject.toml` that carries a `[tool.basilisk]` table. A +`pyproject.toml` **without** the table contributes nothing and does not stop +the walk. + +What the visited tables combine to, and how: + +- **Rule severities are never merged.** The *nearest* table that decides a + rule wins outright — see [Severity resolution](#severity-resolution). +- **Non-rule settings** (paths, versions, typeshed keys) resolve per key: the + nearest file that sets the key wins, keys a nearer file doesn't set come + from the ancestors. `stub-paths` is the one additive key — entries append, + deduplicated. > **Migrating from `basilisk.json`?** The legacy root-level `basilisk.json` > file is no longer read. Translate its keys into `[tool.basilisk]` @@ -26,54 +34,166 @@ it never replaces it. > the file. The configuration editor reports a stray `basilisk.json` as an > ignored shadowed source. -## Minimal configuration +## Zero configuration -```toml -[tool.basilisk] -python-version = "3.12" -``` +Basilisk needs no configuration file at all. With no `[tool.basilisk]` table +anywhere (or an empty one — the two behave identically): -That's all you need. Basilisk finds Python files from the current directory and applies its default rule set — the **core PEP conformance rules**. Extra Basilisk rules that go beyond the spec are opt-in; enable them when you want stricter-than-spec checking. +- Every **core PEP conformance rule** runs at `error` severity. Basilisk's + own opt-in house rules stay off. +- Files are discovered from the current directory. +- The target Python version is resolved from your project files: + `.python-version`, then the `[project].requires-python` lower bound, then + the `uv.lock` `requires-python` lower bound. +- Standard-library stubs are acquired at runtime from the latest + [python/typeshed](https://github.com/python/typeshed) commit, with a + bundled snapshot as the offline fallback — see + [Standard-library stubs](#standard-library-stubs-typeshed). ## Full configuration example ```toml [tool.basilisk] -python-version = "3.12" -python-platform = "All" -stub-paths = ["stubs/"] -typeshed-path = "typeshed-micropython" # optional: replace the bundled stdlib typeshed +python-version = "3.12" # only consulted where a PEP is version-dependent +python-platform = "All" # explicit cross-platform analysis +stub-paths = ["stubs/"] # resolution step 1: prepend extra .pyi stub dirs include = ["src/", "tests/"] exclude = ["**/migrations/**", "**/generated/**"] +# typeshed-commit = "" # pin the stdlib stub source +# typeshed-path = "vendor/typeshed" # or: your own stdlib stub tree [tool.basilisk.rules] -"BSK-0001" = "warning" # selects this opt-in rule at warning -"imports_unresolved" = "info" -"dataclasses_order" = "disabled" +"imports_unresolved" = "warning" # a PEP rule graded down — never disabled +"BSK-0050" = "error" # one house rule promoted above its tag entry -[tool.basilisk.per-path-overrides."legacy/**"] -disabled = ["returns_compatibility"] -rules."imports_unresolved" = "warning" +[tool.basilisk.rule-tags] +"basilisk" = "error" # every house rule on — strict in one line ``` --- -## `[tool.basilisk]` +## Rules: two flat maps + +Rule configuration is two flat maps and nothing else +([`CHKARCH-CONFIG-MODEL`](https://github.com/Nimblesite/Basilisk/blob/main/docs/specs/CHECKER-ARCHITECTURE-SPEC.md#CHKARCH-CONFIG-MODEL)): +per-rule entries and tag entries. Rule codes carry no severity class — a code +is `BSK-nnnn` or a conformance snake_case name like `imports_unresolved`; only +the config entry carries the severity. + +### `[tool.basilisk.rules]` + +Explicit per-rule entries, `"" = ""`: + +```toml +[tool.basilisk.rules] +"imports_unresolved" = "warning" +"BSK-0050" = "error" +"BSK-0001" = "info" +``` + +Accepted severities are `"error"`, `"warning"`, `"info"`, and `"disabled"`. +For an opt-in rule, any non-disabled value also *selects* it — no second +switch is required. Browse every code in the generated +[rule reference](/docs/rules/). + +### `[tool.basilisk.rule-tags]` + +Explicit group entries, `"" = ""` — one written line that +grades **every rule carrying that tag**: + +```toml +[tool.basilisk.rule-tags] +"basilisk" = "error" # every opt-in house rule on +"suppressions" = "warning" # the suppression-audit family at warning +``` + +A tag entry is real configuration in the file — never an implicit mode or +hidden switch. The canonical tag vocabulary: + +- **Provenance:** `pep` (the core conformance rules that run by default) and + `basilisk` (the opt-in house rules). +- **PEP categories**, matching the + [conformance suite's](https://github.com/python/typing/tree/main/conformance/tests) + own file naming: `aliases`, `annotations`, `callables`, `classes`, + `constructors`, `dataclasses`, `directives`, `enums`, `exceptions`, + `generics`, `historical`, `literals`, `namedtuples`, `narrowing`, + `overloads`, `protocols`, `qualifiers`, `specialtypes`, `tuples`, + `typeddicts`, `typeforms`. +- **Descriptive tags** on house rules: `style`, `redundancy`, `strictness`, + `dependencies`, `imports`, `stubs`, `suppressions`. + +The [rule reference](/docs/rules/) lists each rule's tags; the configuration +editor's tag actions write these same `rule-tags` lines. + +### Severity resolution + +Per rule, per checked file — one walk, first decision wins: + +1. Walk from the file's folder to the root. The **nearest** `[tool.basilisk]` + table that decides the rule wins outright. +2. Within one table, a per-rule entry beats tag entries; among matching tag + entries the **strictest** severity wins + (`error` > `warning` > `info` > `disabled`). +3. If no table decides the rule: `pep`-tagged rules run at `error`; every + other rule is disabled. + +That is the whole model — no inherited rule state, no precedence scores, no +merge rules between tables. + +### PEP rules are graded, never disabled + +`disabled` never applies to a `pep`-tagged rule. A configuration that +resolves a PEP rule to `disabled` — whether by rule entry or tag entry — is +**invalid**: the CLI and the editor surface it as a configuration error, and +the checker keeps the rule running regardless, so a conformance diagnostic is +never silently lost. To quiet a PEP rule, grade it to `"warning"` or +`"info"`, suppress specific lines with `# type: ignore` +([below](#inline-suppressions)), or `exclude` the paths. + +### Scoping rules to part of the tree + +There are **no** glob path patterns, per-path override tables, or per-module +exceptions in rule configuration. Scoping a rule differently for part of the +tree means placing a `pyproject.toml` with a `[tool.basilisk]` table in that +folder — the nearest deciding table wins per rule: + +```toml +# pyproject.toml (repo root) +[tool.basilisk.rule-tags] +"basilisk" = "error" + +# tests/pyproject.toml +[tool.basilisk.rules] +"BSK-0001" = "disabled" # opt-in rule off again for everything under tests/ +``` + +--- + +## `[tool.basilisk]` settings ### `python-version` -**Type:** `string` -**Default:** auto-detected from the interpreter on PATH, or `"3.12"` if not found -**Example:** `"3.12"` +**Type:** `string`, e.g. `"3.12"` +**Default:** _(unset — resolved from project files: `.python-version` → `[project].requires-python` lower bound → `uv.lock` `requires-python` lower bound)_ -The Python version to target for type checking. Affects which PEPs and typing features are available. Supports versions `"3.9"` through `"3.14"`. +The Python version the checked code targets. Basilisk has no canonical Python +release: a rule consults this version **only** where the +[typing specification](https://typing.python.org/en/latest/spec/index.html), +an accepted PEP, or Python language semantics makes the answer +version-dependent — for example, [PEP 695](https://peps.python.org/pep-0695/) +`type X = ...` / `class C[T]` syntax is rejected when the target is below +3.12, because the target interpreter cannot parse it. Version-independent +rules never branch on this value. ### `python-platform` **Type:** `"Linux" | "macOS" | "Windows" | "All"` -**Default:** `"All"` +**Default:** _(unset — the selected project interpreter is asked for its `sys.platform`)_ -Target platform. Affects platform-specific type stubs and conditional imports. +Target platform for platform-dependent stubs and `sys.platform` narrowing. +When unset, Basilisk probes the project interpreter and uses that concrete +platform; if the probe fails the platform stays unknown — Basilisk never +invents one. An explicit `"All"` keeps cross-platform intersection semantics. ### `stub-paths` @@ -81,30 +201,25 @@ Target platform. Affects platform-specific type stubs and conditional imports. **Default:** `[]` **Example:** `["stubs/", "typings/"]` -Additional directories to search for `.pyi` stub files. These sit at the **head** of the import search path — step 1 of the [typing spec's import-resolution ordering](https://typing.python.org/en/latest/spec/distributing.html#import-resolution-ordering) — so they can patch or shadow any later module, standard-library or third-party. Useful for custom stubs for internal libraries. - -### `typeshed-path` - -**Type:** `string` -**Default:** _(unset — the bundled typeshed is used)_ -**Example:** `"typeshed-micropython"` -**Spec:** [`STUBRES-CUSTOM-TYPESHED`](https://github.com/Nimblesite/Basilisk/blob/main/docs/specs/CHECKER-STUB-RESOLUTION-SPEC.md#STUBRES-CUSTOM-TYPESHED) - -Path to a directory containing a custom or modified version of typeshed's standard-library stubs. When set, this directory becomes the **canonical source for standard-library types** — step 3 of the [typing spec's import-resolution ordering](https://typing.python.org/en/latest/spec/distributing.html#import-resolution-ordering), which states that type checkers "SHOULD use this as the canonical source for standard-library types in this step." Basilisk resolves stdlib modules against it in preference to the bundled typeshed; a stdlib module absent from the directory falls through to the remaining resolution steps. - -The directory must follow typeshed's layout — standard-library stubs live under a top-level `stdlib/` subdirectory, so Basilisk resolves each module as `/stdlib/.pyi`. A clone of the [python/typeshed](https://github.com/python/typeshed) repository, or any directory you already use as Pyright's [`typeshedPath`](https://microsoft.github.io/pyright/#/configuration) or mypy's [`custom_typeshed_dir`](https://mypy.readthedocs.io/en/stable/config_file.html), works unchanged. Relative paths resolve against the project root. - -Use this to type-check against an alternative standard library — for example MicroPython's [`micropython-stdlib-stubs`](https://github.com/Josverl/micropython-stubs), whose `os`, `time`, and `machine` signatures differ from CPython. Symbols resolved from the custom typeshed hover with a `(custom typeshed)` tag — distinct from the bundled typeshed's `(typeshed)` — so you can confirm the override is active and know a MicroPython signature is never misreported as CPython's. - -`stub-paths` *prepends* extra stub directories; `typeshed-path` *replaces* the vendored standard library wholesale. They are independent and can be combined. See [How to use a custom typeshed](#how-to-use-a-custom-typeshed) below for a step-by-step walkthrough. +Additional directories to search for `.pyi` stub files. These sit at the +**head** of the import search path — step 1 of the +[typing spec's import-resolution ordering](https://typing.python.org/en/latest/spec/distributing.html#import-resolution-ordering) +— so they can patch or shadow any later module, standard-library or +third-party. Useful for custom stubs for internal libraries. Across nested +config files this is the one additive key: nearer entries append to inherited +ones (deduplicated). ### `include` **Type:** `string[]` -**Default:** `["."]` (current directory) +**Default:** _(unset — the current directory is scanned)_ **Example:** `["src/", "tests/"]` -Directories or files to analyze. Plain paths relative to the project root — unlike `exclude`, `include` does **not** accept glob patterns. Only `.py` files are processed. +The roots scanned when no paths are given on the CLI. Plain paths — unlike +`exclude`, `include` does **not** accept glob patterns — resolved relative to +the config file's directory. Explicit CLI paths override it; `exclude` +applies within the include roots. The LSP honors the same roots, so the +editor analyses exactly the files `basilisk check` would. ### `exclude` @@ -122,9 +237,15 @@ exclude = [ **Example:** `["py-gen", "**/generated/**", "*.pb.py"]` -Gitignore-style glob patterns for paths to skip. Hidden directories (names starting with `.`) are always skipped regardless of this setting. +Gitignore-style glob patterns for paths to skip. Hidden directories (names +starting with `.`) are always skipped regardless of this setting, and the +editor's bulk workspace scan additionally always skips the built-in +vendored/cache directory names above. -> **`exclude` _replaces_ the defaults — it does not extend them.** As soon as you set `exclude`, the built-in list above no longer applies. Re-list any defaults you still want alongside your own patterns, or they'll be analyzed again. +> **`exclude` _replaces_ the defaults — it does not extend them.** As soon as +> you set `exclude`, the built-in list above no longer applies to the CLI's +> file discovery. Re-list any defaults you still want alongside your own +> patterns. Pattern syntax, matched against each path relative to the project root: @@ -136,17 +257,90 @@ Pattern syntax, matched against each path relative to the project root: | `gen?.py` | `?` — exactly one character within a segment | | `src/generated` | an **anchored** pattern (contains `/`) — the path or any ancestor dir, plus its subtree | -The same patterns are honoured everywhere Basilisk discovers files: the LSP workspace scan, the `basilisk check` / `fix` / `adopt` CLI, and the editor's per-file checks when you open or edit a file — so a file excluded on the CLI is also silent in the editor. See `CHKARCH-CONFIG-EXCLUDE` in the architecture spec for the canonical semantics. +One canonical matcher is shared by every entry point — the LSP workspace +scan, the `basilisk check` / `fix` / `adopt` CLI, and the editor's per-file +checks — so a path excluded on the CLI is also silent in the editor. See +[`CHKARCH-CONFIG-EXCLUDE`](https://github.com/Nimblesite/Basilisk/blob/main/docs/specs/CHECKER-ARCHITECTURE-SPEC.md#CHKARCH-CONFIG-EXCLUDE) +for the canonical semantics. + +### `narrow-attributes-across-calls` + +**Type:** `bool` +**Default:** `true` + +Whether attribute narrowing (`if x.attr is not None:` guards) survives +intervening function calls. The default is the *usable* behavior: a call +**could** invalidate the attribute, but treating every call as an +invalidation makes attribute narrowing useless in practice. Set to `false` +for the sound-but-strict behavior where any call discards attribute +narrowing. See +[`TYPEINF-NARROWING-ATTR-CALLS`](https://github.com/Nimblesite/Basilisk/blob/main/docs/specs/CHECKER-TYPE-INFERENCE-SPEC.md#TYPEINF-NARROWING-ATTR-CALLS). --- +## Standard-library stubs (typeshed) + +Standard-library types come from +[typeshed](https://github.com/python/typeshed) stubs — step 3 of the +[typing spec's import-resolution ordering](https://typing.python.org/en/latest/spec/distributing.html#import-resolution-ordering). +Basilisk selects exactly **one** step-3 source: + +| Mode | Active source | +| --- | --- | +| Custom folder | your `typeshed-path` directory, verbatim | +| Exact commit | the `typeshed-commit` SHA, downloaded as a verified archive (fails closed if unavailable) | +| Latest _(default)_ | the current `python/typeshed@main` commit, resolved once per run/session; if it cannot be resolved, the bundled snapshot with a warning | + +Latest keeps you fresh but is not reproducible day-to-day — the editor's +Server Info panel reports it as `UNPINNED` and offers **Pin current**, which +writes the resolved SHA as `typeshed-commit`. Downloaded archives pass +safety, shape, license, and content-verification gates before activation, and +are cached as immutable ZIPs. Full detail: +[`STUBRES-TYPESHED`](https://github.com/Nimblesite/Basilisk/blob/main/docs/specs/CHECKER-STUB-RESOLUTION-SPEC.md#STUBRES-TYPESHED). + +| Key | Type | Default | Meaning | +| --- | --- | --- | --- | +| `typeshed-commit` | full 40-char SHA | _(unset — Latest)_ | Exact `python/typeshed` commit to use. A pin **fails closed** — it never silently substitutes another commit. Abbreviated SHAs are rejected. | +| `typeshed-url` | URL template | GitHub codeload | HTTPS archive mirror containing exactly one `{sha}` placeholder. A mirror cannot resolve Latest. | +| `typeshed-cache-path` | path | OS cache dir | Where gate-accepted ZIPs are cached. | +| `typeshed-cache` | bool | `true` | Reuse the re-hashed cached ZIP for 24 hours; `false` downloads, validates, and discards every run. | +| `typeshed-verify` | bool | `true` | Content-attest the archive against the trusted git tree; `false` reports `UNVERIFIED` and never bypasses the safety, shape, or license gates. | +| `typeshed-path` | path | _(unset)_ | Your own stdlib stub tree — disables both download and the bundled snapshot. | + +`typeshed-path` and `typeshed-commit` are **one source selection**: a nested +config file that sets either replaces the inherited choice as a unit, never +mixing a path from one file with a pin from another. + +### `typeshed-path` + +**Type:** `string` +**Default:** _(unset — typeshed is acquired at runtime as above)_ +**Example:** `"vendor/typeshed"` +**Spec:** [`STUBRES-CUSTOM-TYPESHED`](https://github.com/Nimblesite/Basilisk/blob/main/docs/specs/CHECKER-STUB-RESOLUTION-SPEC.md#STUBRES-CUSTOM-TYPESHED) + +Path to a directory containing a custom or modified version of typeshed's +standard-library stubs. When set, this directory becomes the **canonical +source for standard-library types** — the typing spec states that type +checkers "SHOULD use this as the canonical source for standard-library types +in this step." A stdlib module absent from the directory falls through to the +remaining resolution steps — it is **not** rescued by a downloaded or bundled +typeshed. + ## How to use a custom typeshed -`typeshed-path` swaps the standard-library stubs Basilisk bundles for your own copy. Reach for it when you target an alternative Python whose standard library differs from CPython (MicroPython, a patched CPython, a vendor SDK), or when you need a newer or forked typeshed than the one baked into your Basilisk release. +`typeshed-path` swaps the standard-library stubs for your own copy. Reach for +it when you target an alternative Python whose standard library differs from +CPython (MicroPython, a patched CPython, a vendor SDK), or when you need a +forked typeshed rather than an official commit (for an official commit, pin +`typeshed-commit` instead). ### 1. Lay the directory out like typeshed -Point `typeshed-path` at the **root** of a typeshed-layout directory. Standard-library stubs must sit under a top-level `stdlib/` subdirectory, exactly as in the [python/typeshed](https://github.com/python/typeshed) repository — Basilisk resolves each module as `/stdlib/.pyi`: +Point `typeshed-path` at the **root** of a typeshed-layout directory. +Standard-library stubs must sit under a top-level `stdlib/` subdirectory, +exactly as in the [python/typeshed](https://github.com/python/typeshed) +repository — Basilisk resolves each module as +`/stdlib/.pyi`: ``` vendor/typeshed/ @@ -156,11 +350,16 @@ vendor/typeshed/ └── ... ``` -Any directory you already use as Pyright's [`typeshedPath`](https://microsoft.github.io/pyright/#/configuration) or mypy's [`custom_typeshed_dir`](https://mypy.readthedocs.io/en/stable/config_file.html) consumes this same layout, so it works with Basilisk unchanged. +Any directory you already use as Pyright's +[`typeshedPath`](https://microsoft.github.io/pyright/#/configuration) or +mypy's +[`custom_typeshed_dir`](https://mypy.readthedocs.io/en/stable/config_file.html) +consumes this same layout, so it works with Basilisk unchanged. -### 2a. Point at a forked or newer typeshed +### 2a. Point at a forked typeshed -Clone the typeshed repo (or your fork of it), then point `typeshed-path` at the clone and patch the `.pyi` files you need: +Clone the typeshed repo (or your fork of it), then point `typeshed-path` at +the clone and patch the `.pyi` files you need: ```sh git clone https://github.com/python/typeshed vendor/typeshed @@ -171,11 +370,16 @@ git clone https://github.com/python/typeshed vendor/typeshed typeshed-path = "vendor/typeshed" ``` -Basilisk now type-checks the standard library against `vendor/typeshed/stdlib/` instead of its bundled copy. +Basilisk now type-checks the standard library against +`vendor/typeshed/stdlib/`. ### 2b. Point at MicroPython's standard library -MicroPython's stdlib diverges from CPython — `os`, `time`, and `machine` carry different signatures. Install [`micropython-stdlib-stubs`](https://github.com/Josverl/micropython-stubs) (a typeshed-layout copy of the stdlib with MicroPython-specific edits) and point at it: +MicroPython's stdlib diverges from CPython — `os`, `time`, and `machine` +carry different signatures. Install +[`micropython-stdlib-stubs`](https://github.com/Josverl/micropython-stubs) +(a typeshed-layout copy of the stdlib with MicroPython-specific edits) and +point at it: ```toml [tool.basilisk] @@ -183,12 +387,16 @@ python-version = "3.12" typeshed-path = ".venv/lib/python3.12/site-packages/micropython_stdlib_stubs" ``` -Because `micropython-stdlib-stubs` is a **partial** stdlib, a module it does not ship (e.g. `tkinter`, which does not exist on a board) is **not** rescued by the bundled CPython stub — the custom typeshed is the canonical source for step 3, so the import is reported as unresolved. That is the honest answer for an embedded target. +Because `micropython-stdlib-stubs` is a **partial** stdlib, a module it does +not ship (e.g. `tkinter`, which does not exist on a board) is **not** rescued +by a CPython stub — the custom typeshed is the canonical source for step 3, +so the import is reported as unresolved. That is the honest answer for an +embedded target. ### 3. Configure it in the active project file -`typeshed-path` lives in `[tool.basilisk]` like every other setting — there is -no second spelling and no second file. Set it in the `pyproject.toml` that +`typeshed-path` lives in `[tool.basilisk]` like every other setting — there +is no second spelling and no second file. Set it in the `pyproject.toml` that governs the files you are checking (the nearest ancestor with a `[tool.basilisk]` table). Editors do not carry a second copy; that project config file is authoritative. If you are migrating a legacy `basilisk.json`, @@ -196,7 +404,11 @@ its camelCase `typeshedPath` key becomes `typeshed-path` here. ### 4. Confirm it took effect — hover provenance -Symbols resolved from a custom typeshed hover with a `(custom typeshed)` tag, distinct from the bundled typeshed's `(typeshed)` tag. Hover over an imported stdlib symbol: seeing `(custom typeshed)` confirms the override is active and that the signature came from your directory — a MicroPython `os.uname` is never misreported as CPython's. +Symbols resolved from a custom typeshed hover with a `(custom typeshed)` tag, +distinct from the official source's `(typeshed)` tag. Hover over an imported +stdlib symbol: seeing `(custom typeshed)` confirms the override is active and +that the signature came from your directory — a MicroPython `os.uname` is +never misreported as CPython's. ### `typeshed-path` vs `stub-paths` @@ -204,96 +416,25 @@ They solve different problems and can be combined: | | `stub-paths` (step 1) | `typeshed-path` (step 3) | | --- | --- | --- | -| Role | *Prepends* extra `.pyi` directories at the head of the search path | *Replaces* the bundled standard-library typeshed wholesale | +| Role | *Prepends* extra `.pyi` directories at the head of the search path | *Replaces* the standard-library typeshed wholesale | | Scope | Can shadow any single module, stdlib or third-party | Canonical source for the entire standard library | -| Typical use | Patch one broken stub; stubs for an internal library | Target an alternative or forked stdlib (MicroPython, a newer typeshed) | +| Typical use | Patch one broken stub; stubs for an internal library | Target an alternative or forked stdlib (MicroPython, a patched tree) | | Precedence | Wins — a `stub-paths` module still shadows the custom typeshed | Sits below `stub-paths`, above installed packages | --- -## Rule selection and global severity - -The unconfigured default enables the complete core PEP rule set. Basilisk- -specific house rules are tagged `basilisk` and stay off until a project opts in. -There is no ambient basic/standard/strict mode. The editor's **Strict -preset** is a one-shot recipe that writes every live rule's native severity -explicitly into the active config file; after applying it, each rule remains -independently configurable. - -Every opt-in rule is selected by assigning that rule a non-disabled severity: - -```toml -[tool.basilisk.rules] -"BSK-0001" = "error" # required parameter annotations -"BSK-0025" = "error" # required @override -"BSK-0011" = "warning" # undeclared dependency imports -"BSK-0152" = "error" # missing type stubs -``` - -There are no family switches in the project or editor settings. Use the -generated [rule reference](/docs/rules/) or configuration editor to browse the -canonical tags; tag actions expand to explicit rule entries in this file. - -### `[tool.basilisk.rules]` - -Set a rule's global severity. For an opt-in rule, any non-disabled value also -selects it: - -```toml -[tool.basilisk.rules] -"imports_unresolved" = "warning" -"BSK-0050" = "error" -"dataclasses_order" = "disabled" -``` - -Accepted values are `"error"`, `"warning"`, `"info"`, and `"disabled"`. -For an opt-in rule, `"error"`, `"warning"`, or `"info"` selects that individual -rule; `"disabled"` keeps it off. No second switch is required. - ---- - -## `[tool.basilisk.per-path-overrides.""]` - -Apply different settings to specific paths. The glob is matched against file paths relative to the project root. - -```toml -[tool.basilisk.per-path-overrides."legacy/**"] -# Turn rules off entirely for matching files -disabled = ["returns_compatibility"] - -[tool.basilisk.per-path-overrides."tests/**"] -# Or soften a rule's severity instead of disabling it -rules."returns_compatibility" = "warning" -``` - -### `disabled` - -**Type:** `string[]` -**Example:** `["returns_compatibility", "BSK-0001"]` - -Rule codes to disable entirely for files matching this glob. - -### `rules` - -**Type:** table of rule code → severity -**Severities:** `"error"`, `"warning"`, `"info"`, `"disabled"` -**Example:** `rules."returns_compatibility" = "warning"` - -Override the severity of specific rules for matching files. Prefer softening or disabling individual rules over relaxing broad swaths of checking. - ---- - ## Inline suppressions -Use the standard `# type: ignore` spelling. A Basilisk rule code makes the -suppression specific: +Use the standard +[`# type: ignore`](https://typing.python.org/en/latest/spec/directives.html#type-ignore-comments) +spelling. A Basilisk rule code makes the suppression specific: ```python -result: Any = get_legacy_value() # type: ignore[returns_compatibility] +result = get_legacy_value() # type: ignore[returns_compatibility] ``` -Bare or foreign-checker ignore codes follow PEP 484 compatibility behavior and -suppress all diagnostics on the line: +Bare or foreign-checker ignore codes follow PEP 484 compatibility behavior +and suppress all diagnostics on the line: ```python data = unsafe_cast(value) # type: ignore @@ -307,7 +448,19 @@ value = legacy_call() # type: info[returns_compatibility] value = legacy_call() # type: disabled[returns_compatibility] ``` -File-level directives are standalone comments: +A directive on its own line opens a **block**, closed by the matching +`end-` directive: + +```python +# type: disabled[imports_unresolved] +from fastmcp import FastMCP +from result import Result +# type: end-disabled[imports_unresolved] +``` + +File-level directives are standalone comments — `relaxed` grades every error +in the file down to a warning; the `file-` forms apply one effect to specific +codes (or, with no codes, to every rule): ```python # basilisk: relaxed @@ -315,45 +468,33 @@ File-level directives are standalone comments: # basilisk: file-disabled[imports_unresolved] ``` -Suppression auditing is an **opt-in** tagged rule family. It emits nothing by +Suppression auditing is an **opt-in** tagged rule family +(`"suppressions"` in `[tool.basilisk.rule-tags]`). It emits nothing by default. Configure `BSK-0060` (active specific), `BSK-0061` (active blanket), `BSK-0062` (unused), and `BSK-0063` (malformed) independently at error, -warning, info, or disabled. See the -[configuration-editor specification](https://github.com/Nimblesite/Basilisk/blob/main/docs/specs/LSP-CONFIGURATION-EDITOR-SPEC.md#CONFIGEDITOR-SUPPRESSIONS). +warning, info, or disabled. --- -## Configuration discovery - -Basilisk discovers configuration per checked file by walking **up** from the -file's directory. Every ancestor `pyproject.toml` that carries a -`[tool.basilisk]` table contributes, and the tables merge cumulatively: where -the same key is set in more than one file, the **nearest** file wins. Keys a -child table does not set continue to come from the ancestors, so a nested -`pyproject.toml` refines the root configuration — it never blows the root -config away. - -The legacy root-level `basilisk.json` is **never** read. If one is still -present, the configuration editor reports it as an ignored shadowed source; -translate its keys into `[tool.basilisk]` and delete the file. +## Adoption debt -If no ancestor `pyproject.toml` carries a `[tool.basilisk]` table, Basilisk uses defaults: the **core PEP conformance rule set** enabled (extra Basilisk rules stay opt-in), `python-version = "3.12"`, check the current directory. +`basilisk adopt` records a folder's existing diagnostics as ordinary +warning-severity `[tool.basilisk.rules]` entries in the active config file — +no sidecar files, markers, or hidden state. `basilisk unadopt` deletes those +entries, and re-running `adopt` recomputes them, so rules that no longer fire +revert to their full severity automatically. --- ## Visual configuration editor -The tag-first VS Code editor reads the live rule catalog from the LSP, previews -all/tag/rule bulk changes, exposes every rule's effective and explicit severity, -and makes opt-in suppression diagnostics searchable across the workspace. Its -LSP-advertised Strict preset turns the complete catalog on at each rule's native -severity and persists the expanded rule entries—not a mode flag. Safe fixes are -a separate root-scoped LSP action, so applying a preset never hides source edits -inside a config transaction. - -Generated adoption debt is stored as ordinary exact-file `per-path-overrides` -entries in this same active config file—no `.basilisk/adoptions.toml`, hidden -state, or adoption mode. The VSIX does not parse or write configuration itself. +The tag-first VS Code editor reads the live rule catalog from the LSP, +previews bulk changes, and shows every rule's effective severity and where it +was decided. Its edits are typed mutations the LSP applies to the active +`pyproject.toml` — set or remove a rule entry, a tag entry, or a typeshed +setting; requesting `disabled` for a PEP rule is rejected as an error. The +extension never parses or writes configuration files itself, and folder +configs back the editor's scoped-grading view. ![Basilisk's tag-first VS Code configuration editor, showing live rule facets and per-rule severity controls](/assets/images/vscode-configuration-editor.png) diff --git a/website/src/docs/index.md b/website/src/docs/index.md index c97fd16d..ae612a5e 100644 --- a/website/src/docs/index.md +++ b/website/src/docs/index.md @@ -56,12 +56,13 @@ Basilisk's behaviour is decided entirely by **configuration**, and the default c Stricter-than-spec checking is **opt-in**. Basilisk also ships extra rules the spec doesn't define — *require an annotation* on every parameter and return, a redundant-annotation warning, a missing-`@override` nudge, an explicit-`Any` nudge. They stay **off** until you enable them in config. Because they flag code the spec considers valid, turning them on deliberately trades strict spec conformance for a stricter standard of your team's choosing — a per-project choice, never a default. -Configuration is also where you relax rules for the paths that need it — for example, softening or disabling a rule across a legacy directory: +Configuration is also where you relax rules for the paths that need it — place a `pyproject.toml` with a `[tool.basilisk]` table in the folder, and the nearest deciding table wins for the files beneath it: ```toml -[tool.basilisk.per-path-overrides."legacy/**"] -disabled = ["returns_compatibility"] # turn a rule off for legacy code -rules."imports_unresolved" = "warning" # or just soften its severity +# legacy/pyproject.toml +[tool.basilisk.rules] +"returns_compatibility" = "warning" # graded down for legacy code only +"imports_unresolved" = "info" ``` This keeps the default honest — pure spec conformance — while letting each team dial strictness exactly where they want it. @@ -93,7 +94,7 @@ Basilisk is a Cargo workspace with 18 Rust crates, each owning one layer of the ## Next steps -- [Install Basilisk](/docs/installation/) — Homebrew, Scoop, your editor's marketplace, or build from source +- [Install Basilisk](/docs/installation/) — PyPI (`uv tool install`), Homebrew, Scoop, your editor's marketplace, or build from source - [Quick Start](/docs/quick-start/) — your first type check in under 5 minutes - [Refactoring](/docs/refactoring/) — the full refactoring suite (extract, inline, move, rename, convert) - [Debugging](/docs/debugging/) — set breakpoints, step through code, inspect variables diff --git a/website/src/docs/install-cli.md b/website/src/docs/install-cli.md index ec70033a..49ba27c0 100644 --- a/website/src/docs/install-cli.md +++ b/website/src/docs/install-cli.md @@ -1,10 +1,10 @@ --- layout: layouts/docs.njk -title: "Install the Basilisk CLI — Homebrew, Scoop & Binaries" -description: "Install the Basilisk Python type checker as a standalone CLI via Homebrew, Scoop, pre-built binaries, or from source. A single Rust binary with no runtime dependencies — ideal for CI." -keywords: basilisk, cli, homebrew, scoop, binary, install, rust, python type checker, ci, github actions +title: "Install the Basilisk CLI — PyPI, Homebrew, Scoop & Binaries" +description: "Install the Basilisk Python type checker as a standalone CLI via PyPI (uv tool install or pipx), Homebrew, Scoop, pre-built binaries, or from source. A single Rust binary with no runtime dependencies — ideal for CI." +keywords: basilisk, cli, pypi, pip, uv, pipx, homebrew, scoop, binary, install, rust, python type checker, ci, github actions date: 2026-02-28 -dateModified: 2026-03-31 +dateModified: 2026-07-19 author: The Basilisk Project eleventyNavigation: key: CLI & Package Managers @@ -18,6 +18,18 @@ Use these methods when you want the `basilisk` binary on its own — for the com > Using **VS Code, Cursor, or Windsurf**? The binary is bundled in the extension — see [VS Code & Cursor](/docs/install-vscode/). Using **Zed**? The binary downloads with the extension — see [Zed](/docs/install-zed/). Neither needs a separate CLI install. +## PyPI (uv, pipx) + +The wheel [`basilisk-python`](https://pypi.org/project/basilisk-python/) bundles the same native binary that ships via Homebrew, Scoop, and GitHub Releases. Install it as a standalone tool, so the `basilisk` command lands on your PATH without touching any project environment: + +```bash +uv tool install basilisk-python +# or +pipx install basilisk-python +``` + +The installed command is `basilisk` (the distribution is named `basilisk-python` only because `basilisk` was already taken on PyPI). Wheels are published for Linux (x86_64, aarch64), macOS (Apple Silicon), and Windows (x64, arm64). The wheel contains no Python code — it is the same standalone Rust binary, so it works on any CPython or PyPy 3.x. + ## Homebrew (macOS, Linux) ```bash @@ -91,6 +103,8 @@ Basilisk integrates naturally into any CI pipeline. Download the binary in your run: basilisk check src/ ``` +In a pipeline that already has `uv` available, `uv tool install basilisk-python` works just as well as downloading the release binary. + Exit codes: - `0` — No errors found diff --git a/website/src/docs/installation.md b/website/src/docs/installation.md index 3736fd33..89a10696 100644 --- a/website/src/docs/installation.md +++ b/website/src/docs/installation.md @@ -1,10 +1,10 @@ --- layout: layouts/docs.njk title: "Install Basilisk — VS Code, Cursor, Zed, Neovim, or CLI" -description: "Install the Basilisk Python language server for your editor — VS Code, Cursor, Windsurf, Zed, or Neovim — or as a standalone CLI via Homebrew, Scoop, or pre-built binaries. Single Rust binary, no runtime dependencies." -keywords: basilisk, install, vs code, cursor, windsurf, zed, neovim, homebrew, scoop, open vsx, python language server, rust +description: "Install the Basilisk Python language server for your editor — VS Code, Cursor, Windsurf, Zed, or Neovim — or as a standalone CLI via PyPI (uv tool install or pipx), Homebrew, Scoop, or pre-built binaries. Single Rust binary, no runtime dependencies." +keywords: basilisk, install, vs code, cursor, windsurf, zed, neovim, pypi, pip, uv, pipx, homebrew, scoop, open vsx, python language server, rust date: 2026-02-28 -dateModified: 2026-07-12 +dateModified: 2026-07-19 author: The Basilisk Project eleventyNavigation: key: Installation @@ -22,7 +22,7 @@ Pick your setup: | **VS Code, Cursor, Windsurf** | [VS Code & Cursor](/docs/install-vscode/) | bundled inside the extension | | **Zed** | [Zed](/docs/install-zed/) | downloaded with the extension on first run | | **Neovim** | [Neovim](/docs/install-neovim/) | downloaded by the plugin on first use | -| **The command line / CI** | [CLI & Package Managers](/docs/install-cli/) | installed via Homebrew, Scoop, or a release binary | +| **The command line / CI** | [CLI & Package Managers](/docs/install-cli/) | installed via PyPI (`uv tool install`), Homebrew, Scoop, or a release binary | ## Editor support (LSP) diff --git a/website/src/docs/migration.md b/website/src/docs/migration.md index ceb35cb3..a37e4674 100644 --- a/website/src/docs/migration.md +++ b/website/src/docs/migration.md @@ -4,7 +4,7 @@ title: "Migrate to Basilisk from Pyright or mypy" description: "Current, honest steps for moving a Python project to Basilisk with per-rule severity and gradual path-based adoption." keywords: migrate to basilisk, from pyright, from mypy, python type checker migration date: 2026-02-28 -dateModified: 2026-07-14 +dateModified: 2026-07-19 author: The Basilisk Project eleventyNavigation: key: Migration @@ -50,21 +50,22 @@ reports a stray `basilisk.json` as an ignored shadowed source. ## 2. Choose the target policy -The PEP rule set needs no switch. Opt into Basilisk rules with explicit -severities (or apply a configuration-editor preset, which writes these entries): +The PEP rule set needs no switch. Opt into Basilisk rules individually, or +grade a whole tag with one written line: ```toml +[tool.basilisk.rule-tags] +"basilisk" = "error" # every opt-in house rule on + [tool.basilisk.rules] -"BSK-0001" = "error" -"BSK-0025" = "error" -"BSK-0011" = "warning" -"BSK-0152" = "error" +"BSK-0011" = "warning" # then grade individual rules over their tag entry ``` Rules are organised by their live [provenance, PEP-category, and descriptive tags](/docs/rules/). Basilisk has no -basic/standard/strict mode or rule-family switches; the persisted per-rule -severities are the policy. +basic/standard/strict mode; the policy is exactly the rule and tag entries +persisted in the file. See +[Rules: two flat maps](/docs/configuration/#rules-two-flat-maps). ## 3. Run the checker and fix safe debt first @@ -90,19 +91,22 @@ Accepted severities are `error`, `warning`, `info`, and `disabled`. An explicit non-disabled severity also enables an opt-in rule; removing the entry returns it to inherited tag/default selection. -For legacy areas, keep the exception local: +For legacy areas, keep the exception local by placing a `pyproject.toml` with +a `[tool.basilisk]` table in that folder — the nearest deciding table wins per +rule, so the rest of the tree keeps the strict grading: ```toml -[tool.basilisk.per-path-overrides."legacy/**"] -rules."returns_compatibility" = "warning" -rules."imports_unresolved" = "info" - -[tool.basilisk.per-path-overrides."vendor/**"] -disabled = ["imports_unresolved"] +# legacy/pyproject.toml +[tool.basilisk.rules] +"returns_compatibility" = "warning" +"imports_unresolved" = "info" ``` -A project-wide `disabled` entry hides future violations too. Path and per-file -adoption are safer when the debt is confined to existing code. +There are no glob path patterns or per-path override tables — scoped policy is +always a config file in the scoped folder. A project-wide `disabled` entry +hides future violations too (and is invalid for PEP rules, which can only be +graded); `basilisk adopt` is the safer tool when the debt is confined to +existing code. ## 5. Preserve and audit inline ignores @@ -147,7 +151,7 @@ manual mappings are: | `stubPath` | `[tool.basilisk].stub-paths` | | `typeshedPath` | `[tool.basilisk].typeshed-path` | | `report…` severity | `[tool.basilisk.rules]."RULE_CODE"` | -| execution-environment exception | `[tool.basilisk.per-path-overrides."glob"]` where semantics match | +| execution-environment exception | a `pyproject.toml` with `[tool.basilisk]` in that folder | Use the [Pyright configuration reference](https://microsoft.github.io/pyright/#/configuration) to inspect the source value and Basilisk's [rule reference](/docs/rules/) to @@ -165,7 +169,7 @@ Typical manual mappings are: | `exclude` | `[tool.basilisk].exclude` (gitignore-style patterns) | | `mypy_path` | `[tool.basilisk].stub-paths` when it contains stubs | | `custom_typeshed_dir` | `[tool.basilisk].typeshed-path` | -| per-module relaxations | per-path overrides where the exception is source-path based | +| per-module relaxations | a folder-scoped `pyproject.toml` where the exception is source-path based | | `# type: ignore[code]` | keep the syntax, replace the foreign code with a Basilisk code | Consult the [mypy configuration reference](https://mypy.readthedocs.io/en/stable/config_file.html) @@ -178,16 +182,17 @@ than disabling unrelated checks globally. The VS Code configuration editor provides this workflow as one review surface: 1. browse the canonical rule catalog by tags; -2. preview the LSP-advertised **Strict preset** (all rules at native severity) - or “maximum policy”; +2. turn a whole tag on with one `rule-tags` entry (e.g. `"basilisk" = "error"`) + and preview the effect before applying; 3. run Safe fixes through the separate root-scoped LSP action; -4. group remaining debt by tag, rule, file, and fixability; -5. demote/disable selected rules globally or adopt only affected files; -6. preview the exact diff and apply it against a revision token; -7. track exceptions and opt-in suppression diagnostics until they graduate. - -Per-file adoption is stored as exact-file rule severities in the same active -project config. It does not create a second config file or a persistent mode. +4. group remaining debt by tag and rule; +5. grade selected rules down explicitly, or record the debt with + `basilisk adopt`; +6. track exceptions and opt-in suppression diagnostics until they graduate. + +Adoption debt is stored as ordinary warning-severity rule entries in the same +active project config. It does not create a second config file or a persistent +mode. It is [specified](https://github.com/Nimblesite/Basilisk/blob/main/docs/specs/LSP-CONFIGURATION-EDITOR-SPEC.md) and tracked in the [implementation plan](https://github.com/Nimblesite/Basilisk/blob/main/docs/plans/LSP-CONFIGURATION-EDITOR-PLAN.md). diff --git a/website/src/docs/quick-start.md b/website/src/docs/quick-start.md index 786c8477..b0ca5ac2 100644 --- a/website/src/docs/quick-start.md +++ b/website/src/docs/quick-start.md @@ -163,8 +163,8 @@ project is at full strictness. In VS Code, skip the hand-editing: run **Basilisk: Open Configuration Editor** from the Command Palette. It shows every rule with its live severity, previews -changes before applying them, and its **Strict preset** turns the complete -catalog on in one click. See the +changes before applying them, and its tag actions turn a whole rule group on +with one written `rule-tags` line (e.g. `"basilisk" = "error"`). See the [configuration reference](/docs/configuration/) for the full schema. ![Basilisk's tag-first VS Code configuration editor, showing live rule facets and per-rule severity controls](/assets/images/vscode-configuration-editor.png) diff --git a/website/src/index.njk b/website/src/index.njk index 734f0063..068dca4d 100644 --- a/website/src/index.njk +++ b/website/src/index.njk @@ -324,7 +324,7 @@ permalink: / {{ ruleStats.strictness }} opt-in strictness rules refuse that: they require a type on every parameter, return, variable, *args/**kwargs, and class attribute, demand @override on overrides, reject implicit and explicit Any, and flag - unannotated lambdas. Every one is off by default — switch them on per-file or per-path + unannotated lambdas. Every one is off by default — switch them on project-wide or per-folder from your editor or pyproject.toml, and one-click quick-fixes drop in a placeholder annotation (: Any, -> None) so you can fill in the real type. They never run during conformance, so they can’t pad that score. See the rules → diff --git a/website/src/zh/docs/configuration.md b/website/src/zh/docs/configuration.md index 37b466c4..cd925d84 100644 --- a/website/src/zh/docs/configuration.md +++ b/website/src/zh/docs/configuration.md @@ -1,68 +1,183 @@ --- layout: layouts/docs.njk title: 配置参考 -description: Basilisk pyproject.toml 配置选项的完整参考。严重性覆盖、每路径规则、内联抑制和 Ruff 集成。 +description: Basilisk pyproject.toml 配置选项的完整参考。规则与标签严重性、typeshed 来源固定、内联抑制以及按文件夹作用域的配置。 keywords: basilisk, 配置, pyproject.toml, 设置 lang: zh -dateModified: 2026-07-14 +dateModified: 2026-07-19 --- # 配置参考 `pyproject.toml` 中的 `[tool.basilisk]` 是唯一的配置来源。对于每个被检查的 -文件,Basilisk 从该文件所在目录向上遍历,读取每一个带有 `[tool.basilisk]` -表的祖先 `pyproject.toml`。这些表会累积合并:同一个键在多个文件中都有设置 -时,**最近**的文件生效——子目录中的 `pyproject.toml` 只是细化根配置, -绝不会将其整体替换。 +文件,Basilisk 从该文件所在目录向上遍历,访问每一个带有 `[tool.basilisk]` +表的祖先 `pyproject.toml`。**不带**该表的 `pyproject.toml` 不参与配置, +也不会终止遍历。 + +被访问的表如何组合: + +- **规则严重性从不合并。** 对每条规则,*最近*作出决定的表直接胜出——见 + [严重性解析](#严重性解析)。 +- **非规则设置**(路径、版本、typeshed 键)按键解析:最近设置了该键的 + 文件生效,更近的文件未设置的键继续沿用祖先的值。`stub-paths` 是唯一的 + 追加型键——条目追加并去重。 > **正在从 `basilisk.json` 迁移?** 旧版根目录 `basilisk.json` 文件已不再 > 被读取。请将其键翻译为 `[tool.basilisk]`(驼峰式 → 短横线式,例如 > `typeshedPath` → `typeshed-path`),然后删除该文件。配置编辑器会将遗留的 > `basilisk.json` 报告为被忽略的遮蔽来源。 -## 最小配置 +## 零配置 -```toml -[tool.basilisk] -python-version = "3.12" -``` +Basilisk 完全不需要配置文件。当任何地方都没有 `[tool.basilisk]` 表时 +(或表为空——两者行为完全一致): -这就是您所需要的全部。Basilisk 从当前目录查找 Python 文件并应用其默认规则集——**核心 PEP 符合性规则**。超出规范的额外 Basilisk 规则是可选的;当你想要比规范更严格的检查时再启用它们。 +- 每条**核心 PEP 符合性规则**以 `error` 严重性运行;Basilisk 自有的可选 + 规则保持关闭。 +- 从当前目录发现文件。 +- 目标 Python 版本从项目文件解析:`.python-version`,然后是 + `[project].requires-python` 下界,然后是 `uv.lock` 的 + `requires-python` 下界。 +- 标准库存根在运行时从最新的 + [python/typeshed](https://github.com/python/typeshed) 提交获取,离线时 + 回退到内置快照——见[标准库存根](#标准库存根-typeshed)。 ## 完整配置示例 ```toml [tool.basilisk] -python-version = "3.12" -python-platform = "All" -stub-paths = ["stubs/"] -typeshed-path = "typeshed-micropython" # 可选:替换捆绑的标准库 typeshed +python-version = "3.12" # 仅当 PEP 依赖版本时才被查询 +python-platform = "All" # 显式跨平台分析 +stub-paths = ["stubs/"] # 解析第 1 步:前置额外的 .pyi 存根目录 include = ["src/", "tests/"] exclude = ["**/migrations/**", "**/generated/**"] +# typeshed-commit = "<完整 40 位提交 SHA>" # 固定标准库存根来源 +# typeshed-path = "vendor/typeshed" # 或:你自己的标准库存根树 -[tool.basilisk.per-path-overrides."legacy/**"] -disabled = ["returns_compatibility"] -rules."imports_unresolved" = "warning" +[tool.basilisk.rules] +"imports_unresolved" = "warning" # PEP 规则降级——绝不禁用 +"BSK-0050" = "error" # 单条自有规则提升到其标签条目之上 + +[tool.basilisk.rule-tags] +"basilisk" = "error" # 一行启用所有自有规则——严格模式 ``` --- -## `[tool.basilisk]` +## 规则:两个扁平映射 + +规则配置是两个扁平映射,除此之外别无其他 +([`CHKARCH-CONFIG-MODEL`](https://github.com/Nimblesite/Basilisk/blob/main/docs/specs/CHECKER-ARCHITECTURE-SPEC.md#CHKARCH-CONFIG-MODEL)): +逐规则条目和标签条目。规则代码本身不携带严重性——代码是 `BSK-nnnn` 或 +符合性 snake_case 名称(如 `imports_unresolved`);只有配置条目携带 +严重性。 + +### `[tool.basilisk.rules]` + +显式的逐规则条目,`"<代码>" = "<严重性>"`: + +```toml +[tool.basilisk.rules] +"imports_unresolved" = "warning" +"BSK-0050" = "error" +"BSK-0001" = "info" +``` + +可用值为 `"error"`、`"warning"`、`"info"` 与 `"disabled"`。对于可选规则, +任何非 disabled 的值同时也*选择*该规则——无需第二个开关。可在生成的 +[规则参考](/zh/docs/rules/)中浏览所有代码。 + +### `[tool.basilisk.rule-tags]` + +显式的分组条目,`"<标签>" = "<严重性>"`——一行即可为**携带该标签的每条 +规则**定级: + +```toml +[tool.basilisk.rule-tags] +"basilisk" = "error" # 所有可选自有规则全部启用 +"suppressions" = "warning" # 抑制审计规则族设为 warning +``` + +标签条目是写在文件里的真实配置——绝不是隐式模式或隐藏开关。规范标签 +词汇表: + +- **来源标签:** `pep`(默认运行的核心符合性规则)与 `basilisk` + (可选的自有规则)。 +- **PEP 分类**,与[符合性测试套件](https://github.com/python/typing/tree/main/conformance/tests) + 的文件命名一致:`aliases`、`annotations`、`callables`、`classes`、 + `constructors`、`dataclasses`、`directives`、`enums`、`exceptions`、 + `generics`、`historical`、`literals`、`namedtuples`、`narrowing`、 + `overloads`、`protocols`、`qualifiers`、`specialtypes`、`tuples`、 + `typeddicts`、`typeforms`。 +- **描述性标签**(自有规则):`style`、`redundancy`、`strictness`、 + `dependencies`、`imports`、`stubs`、`suppressions`。 + +[规则参考](/zh/docs/rules/)列出每条规则的标签;配置编辑器的标签操作 +写入的正是这些 `rule-tags` 行。 + +### 严重性解析 + +按规则、按被检查文件——一次遍历,首个决定胜出: + +1. 从文件所在文件夹向根目录遍历。**最近**对该规则作出决定的 + `[tool.basilisk]` 表直接胜出。 +2. 在同一个表内,逐规则条目优先于标签条目;多个匹配的标签条目中, + **最严格**的严重性胜出(`error` > `warning` > `info` > `disabled`)。 +3. 没有任何表对该规则作出决定时:带 `pep` 标签的规则以 `error` 运行; + 其他所有规则处于禁用状态。 + +这就是完整的模型——没有继承的规则状态,没有优先级分数,表与表之间 +也没有合并规则。 + +### PEP 规则只能定级,绝不禁用 + +`disabled` 永远不适用于带 `pep` 标签的规则。任何将 PEP 规则解析为 +`disabled` 的配置——无论通过规则条目还是标签条目——都是**无效的**: +CLI 与编辑器会将其呈现为配置错误,且检查器无论如何都会保持该规则运行, +确保符合性诊断绝不会被静默丢失。要让 PEP 规则安静,请将其定级为 +`"warning"` 或 `"info"`,用 `# type: ignore` 抑制特定行 +([见下文](#内联抑制)),或 `exclude` 相关路径。 + +### 将规则作用于目录树的一部分 + +规则配置中**没有** glob 路径模式、逐路径覆盖表或逐模块例外。要让某个 +子树使用不同的规则配置,就在那个文件夹放置带 `[tool.basilisk]` 表的 +`pyproject.toml`——最近作出决定的表按规则胜出: + +```toml +# pyproject.toml(仓库根目录) +[tool.basilisk.rule-tags] +"basilisk" = "error" + +# tests/pyproject.toml +[tool.basilisk.rules] +"BSK-0001" = "disabled" # tests/ 下重新关闭这条可选规则 +``` + +--- + +## `[tool.basilisk]` 设置 ### `python-version` -**类型:** `string` -**默认值:** 从 PATH 上的解释器自动检测,如果未找到则为 `"3.12"` -**示例:** `"3.12"` +**类型:** `string`,例如 `"3.12"` +**默认值:** _(未设置——从项目文件解析:`.python-version` → `[project].requires-python` 下界 → `uv.lock` 的 `requires-python` 下界)_ -用于类型检查的目标 Python 版本。影响哪些 PEP 和类型功能可用。支持 `"3.9"` 到 `"3.14"` 的版本。 +被检查代码的目标 Python 版本。Basilisk 没有规范的 Python 发行版本:只有当 +[typing 规范](https://typing.python.org/en/latest/spec/index.html)、已接受的 +PEP 或 Python 语言语义使结果依赖版本时,规则才会查询此版本——例如 +[PEP 695](https://peps.python.org/pep-0695/) 的 `type X = ...` / +`class C[T]` 语法在目标低于 3.12 时会被拒绝,因为目标解释器根本无法解析 +它。与版本无关的规则从不依据此值分支。 ### `python-platform` **类型:** `"Linux" | "macOS" | "Windows" | "All"` -**默认值:** `"All"` +**默认值:** _(未设置——向所选项目解释器查询其 `sys.platform`)_ -目标平台。影响平台特定的类型存根和条件导入。 +平台相关存根与 `sys.platform` 窄化的目标平台。未设置时,Basilisk 探测项目 +解释器并使用该具体平台;探测失败则平台保持未知——Basilisk 绝不凭空捏造 +平台。显式的 `"All"` 保持跨平台交集语义。 ### `stub-paths` @@ -70,30 +185,22 @@ rules."imports_unresolved" = "warning" **默认值:** `[]` **示例:** `["stubs/", "typings/"]` -用于搜索 `.pyi` 存根文件的额外目录。它们位于导入搜索路径的**最前端**——[typing 规范的导入解析顺序](https://typing.python.org/en/latest/spec/distributing.html#import-resolution-ordering)中的第 1 步——因此可以修补或遮蔽任何后续模块,无论是标准库还是第三方。对于内部库的自定义存根很有用。 - -### `typeshed-path` - -**类型:** `string` -**默认值:** _(未设置——使用捆绑的 typeshed)_ -**示例:** `"typeshed-micropython"` -**规范:** [`STUBRES-CUSTOM-TYPESHED`](https://github.com/Nimblesite/Basilisk/blob/main/docs/specs/CHECKER-STUB-RESOLUTION-SPEC.md#STUBRES-CUSTOM-TYPESHED) - -指向包含 typeshed 标准库存根的自定义或修改版本的目录路径。设置后,该目录将成为**标准库类型的规范来源**——[typing 规范的导入解析顺序](https://typing.python.org/en/latest/spec/distributing.html#import-resolution-ordering)中的第 3 步,该规范指出类型检查器"SHOULD use this as the canonical source for standard-library types in this step"(应将其用作此步骤中标准库类型的规范来源)。Basilisk 优先针对它解析标准库模块,而不是捆绑的 typeshed;目录中缺失的标准库模块将继续进入后续的解析步骤。 - -该目录必须遵循 typeshed 的布局——标准库存根位于顶层 `stdlib/` 子目录下,因此 Basilisk 将每个模块解析为 `/stdlib/.pyi`。[python/typeshed](https://github.com/python/typeshed) 仓库的克隆,或任何你已用作 Pyright 的 [`typeshedPath`](https://microsoft.github.io/pyright/#/configuration) 或 mypy 的 [`custom_typeshed_dir`](https://mypy.readthedocs.io/en/stable/config_file.html) 的目录,都可原样使用。相对路径相对于项目根目录解析。 - -使用此选项可针对替代标准库进行类型检查——例如 MicroPython 的 [`micropython-stdlib-stubs`](https://github.com/Josverl/micropython-stubs),其 `os`、`time` 和 `machine` 签名与 CPython 不同。从自定义 typeshed 解析的符号在悬停时会带有 `(custom typeshed)` 标记——区别于捆绑 typeshed 的 `(typeshed)`——因此你可以确认覆盖已生效,并确保 MicroPython 的签名绝不会被误报为 CPython 的签名。 - -`stub-paths` *前置*额外的存根目录;`typeshed-path` 则*整体替换*捆绑的标准库。二者相互独立,可以组合使用。有关分步演练,请参见下方的"如何使用自定义 typeshed"一节。 +用于搜索 `.pyi` 存根文件的额外目录。它们位于导入搜索路径的**最前端**—— +[typing 规范的导入解析顺序](https://typing.python.org/en/latest/spec/distributing.html#import-resolution-ordering) +中的第 1 步——因此可以修补或遮蔽任何后续模块,无论是标准库还是第三方。 +对于内部库的自定义存根很有用。在嵌套配置文件之间,这是唯一的追加型键: +更近的条目追加到继承的条目上(去重)。 ### `include` **类型:** `string[]` -**默认值:** `["."]`(当前目录) +**默认值:** _(未设置——扫描当前目录)_ **示例:** `["src/", "tests/"]` -要分析的目录或文件。相对于项目根目录的普通路径——与 `exclude` 不同,`include` **不**接受 glob 模式。只处理 `.py` 文件。 +CLI 未给出路径时扫描的根目录。普通路径——与 `exclude` 不同,`include` +**不**接受 glob 模式——相对于配置文件所在目录解析。显式的 CLI 路径会 +覆盖它;`exclude` 在 include 根目录内生效。LSP 遵循相同的根目录,因此 +编辑器分析的正是 `basilisk check` 会分析的那些文件。 ### `exclude` @@ -111,9 +218,13 @@ exclude = [ **示例:** `["py-gen", "**/generated/**", "*.pb.py"]` -用于跳过路径的 gitignore 风格 glob 模式。隐藏目录(名称以 `.` 开头)无论此设置如何都始终会被跳过。 +用于跳过路径的 gitignore 风格 glob 模式。隐藏目录(名称以 `.` 开头) +无论此设置如何都始终会被跳过,且编辑器的批量工作区扫描始终额外跳过上面 +内置的 vendored/缓存目录名。 -> **`exclude` 会_替换_默认值——而不是在其基础上追加。** 一旦你设置了 `exclude`,上面的内置列表便不再生效。请将你仍需要的任何默认值与自己的模式一并重新列出,否则它们会被重新分析。 +> **`exclude` 会_替换_默认值——而不是在其基础上追加。** 一旦你设置了 +> `exclude`,上面的内置列表便不再作用于 CLI 的文件发现。请将你仍需要的 +> 任何默认值与自己的模式一并重新列出。 模式语法,针对每个相对于项目根目录的路径进行匹配: @@ -125,17 +236,81 @@ exclude = [ | `gen?.py` | `?`——段内恰好一个字符 | | `src/generated` | **锚定**模式(包含 `/`)——该路径或其任意祖先目录,及其子树 | -Basilisk 在发现文件的所有场景中都遵循相同的模式:LSP 工作区扫描、`basilisk check` / `fix` / `adopt` CLI,以及你打开或编辑文件时编辑器的逐文件检查——因此在 CLI 上被排除的文件在编辑器中同样静默。规范语义请参见架构规范中的 `CHKARCH-CONFIG-EXCLUDE`。 +所有入口共享同一个规范匹配器——LSP 工作区扫描、`basilisk check` / +`fix` / `adopt` CLI,以及编辑器的逐文件检查——因此在 CLI 上被排除的路径 +在编辑器中同样静默。规范语义请参见 +[`CHKARCH-CONFIG-EXCLUDE`](https://github.com/Nimblesite/Basilisk/blob/main/docs/specs/CHECKER-ARCHITECTURE-SPEC.md#CHKARCH-CONFIG-EXCLUDE)。 + +### `narrow-attributes-across-calls` + +**类型:** `bool` +**默认值:** `true` + +属性窄化(`if x.attr is not None:` 守卫)是否在中间函数调用后仍然保持。 +默认值是*实用*的行为:调用**可能**使属性失效,但把每次调用都当作失效会 +让属性窄化在实践中毫无用处。设为 `false` 可获得健全但严格的行为:任何 +调用都会丢弃属性窄化。参见 +[`TYPEINF-NARROWING-ATTR-CALLS`](https://github.com/Nimblesite/Basilisk/blob/main/docs/specs/CHECKER-TYPE-INFERENCE-SPEC.md#TYPEINF-NARROWING-ATTR-CALLS)。 --- +## 标准库存根(typeshed) + +标准库类型来自 [typeshed](https://github.com/python/typeshed) 存根—— +[typing 规范的导入解析顺序](https://typing.python.org/en/latest/spec/distributing.html#import-resolution-ordering) +中的第 3 步。Basilisk 恰好选择**一个**第 3 步来源: + +| 模式 | 生效来源 | +| --- | --- | +| 自定义文件夹 | 你的 `typeshed-path` 目录,原样使用 | +| 精确提交 | `typeshed-commit` 指定的 SHA,以经验证的归档下载(不可用时失败关闭) | +| 最新(默认) | 当前的 `python/typeshed@main` 提交,每次运行/会话解析一次;无法解析时回退到内置快照并警告 | + +"最新"模式保持新鲜但无法逐日复现——编辑器的 Server Info 面板将其报告为 +`UNPINNED` 并提供 **Pin current** 操作,把解析出的 SHA 写入 +`typeshed-commit`。下载的归档在激活前需通过安全、结构、许可证与内容验证 +关卡,并以不可变 ZIP 缓存。完整细节: +[`STUBRES-TYPESHED`](https://github.com/Nimblesite/Basilisk/blob/main/docs/specs/CHECKER-STUB-RESOLUTION-SPEC.md#STUBRES-TYPESHED)。 + +| 键 | 类型 | 默认值 | 含义 | +| --- | --- | --- | --- | +| `typeshed-commit` | 完整 40 位 SHA | _(未设置——最新)_ | 要使用的精确 `python/typeshed` 提交。固定后**失败关闭**——绝不静默替换为其他提交。缩写 SHA 会被拒绝。 | +| `typeshed-url` | URL 模板 | GitHub codeload | 包含恰好一个 `{sha}` 占位符的 HTTPS 归档镜像。镜像无法解析"最新"。 | +| `typeshed-cache-path` | 路径 | 操作系统缓存目录 | 通过关卡的 ZIP 的缓存位置。 | +| `typeshed-cache` | bool | `true` | 24 小时内复用经重新哈希的缓存 ZIP;`false` 每次运行都下载、验证并丢弃。 | +| `typeshed-verify` | bool | `true` | 对归档做内容证明,与受信任的 git 树比对;`false` 报告 `UNVERIFIED`,且绝不绕过安全、结构或许可证关卡。 | +| `typeshed-path` | 路径 | _(未设置)_ | 你自己的标准库存根树——同时禁用下载与内置快照。 | + +`typeshed-path` 与 `typeshed-commit` 是**同一个来源选择**:设置了其中任一 +键的嵌套配置文件会将继承的选择作为整体替换,绝不会把一个文件的路径和 +另一个文件的固定提交混在一起。 + +### `typeshed-path` + +**类型:** `string` +**默认值:** _(未设置——按上文在运行时获取 typeshed)_ +**示例:** `"vendor/typeshed"` +**规范:** [`STUBRES-CUSTOM-TYPESHED`](https://github.com/Nimblesite/Basilisk/blob/main/docs/specs/CHECKER-STUB-RESOLUTION-SPEC.md#STUBRES-CUSTOM-TYPESHED) + +指向包含 typeshed 标准库存根的自定义或修改版本的目录路径。设置后,该目录 +将成为**标准库类型的规范来源**——typing 规范指出类型检查器"SHOULD use +this as the canonical source for standard-library types in this step" +(应将其用作此步骤中标准库类型的规范来源)。目录中缺失的标准库模块将继续 +进入后续的解析步骤——**不会**由下载的或内置的 typeshed 来兜底。 + ## 如何使用自定义 typeshed -`typeshed-path` 会将 Basilisk 捆绑的标准库存根替换为你自己的副本。当你面向标准库与 CPython 不同的替代 Python(MicroPython、打过补丁的 CPython、厂商 SDK),或需要比 Basilisk 发行版内置版本更新或分叉的 typeshed 时,就可以使用它。 +`typeshed-path` 会将标准库存根替换为你自己的副本。当你面向标准库与 +CPython 不同的替代 Python(MicroPython、打过补丁的 CPython、厂商 SDK), +或需要分叉的 typeshed 而非官方提交时(官方提交请改用固定 +`typeshed-commit`),就可以使用它。 ### 1. 按 typeshed 的方式组织目录 -将 `typeshed-path` 指向 typeshed 布局目录的**根**。标准库存根必须位于顶层 `stdlib/` 子目录下,与 [python/typeshed](https://github.com/python/typeshed) 仓库完全一致——Basilisk 将每个模块解析为 `/stdlib/.pyi`: +将 `typeshed-path` 指向 typeshed 布局目录的**根**。标准库存根必须位于顶层 +`stdlib/` 子目录下,与 [python/typeshed](https://github.com/python/typeshed) +仓库完全一致——Basilisk 将每个模块解析为 +`/stdlib/.pyi`: ``` vendor/typeshed/ @@ -145,11 +320,16 @@ vendor/typeshed/ └── ... ``` -任何你已用作 Pyright 的 [`typeshedPath`](https://microsoft.github.io/pyright/#/configuration) 或 mypy 的 [`custom_typeshed_dir`](https://mypy.readthedocs.io/en/stable/config_file.html) 的目录都采用同样的布局,因此可与 Basilisk 原样配合使用。 +任何你已用作 Pyright 的 +[`typeshedPath`](https://microsoft.github.io/pyright/#/configuration) 或 +mypy 的 +[`custom_typeshed_dir`](https://mypy.readthedocs.io/en/stable/config_file.html) +的目录都采用同样的布局,因此可与 Basilisk 原样配合使用。 -### 2a. 指向分叉或更新的 typeshed +### 2a. 指向分叉的 typeshed -克隆 typeshed 仓库(或你的分叉),然后将 `typeshed-path` 指向该克隆,并修补你需要的 `.pyi` 文件: +克隆 typeshed 仓库(或你的分叉),然后将 `typeshed-path` 指向该克隆, +并修补你需要的 `.pyi` 文件: ```sh git clone https://github.com/python/typeshed vendor/typeshed @@ -160,11 +340,14 @@ git clone https://github.com/python/typeshed vendor/typeshed typeshed-path = "vendor/typeshed" ``` -现在 Basilisk 会针对 `vendor/typeshed/stdlib/` 而不是其捆绑副本对标准库进行类型检查。 +现在 Basilisk 会针对 `vendor/typeshed/stdlib/` 对标准库进行类型检查。 ### 2b. 指向 MicroPython 的标准库 -MicroPython 的标准库与 CPython 存在差异——`os`、`time` 和 `machine` 的签名各不相同。安装 [`micropython-stdlib-stubs`](https://github.com/Josverl/micropython-stubs)(一份带有 MicroPython 特定改动的 typeshed 布局标准库副本)并将其指向它: +MicroPython 的标准库与 CPython 存在差异——`os`、`time` 和 `machine` 的 +签名各不相同。安装 +[`micropython-stdlib-stubs`](https://github.com/Josverl/micropython-stubs) +(一份带有 MicroPython 特定改动的 typeshed 布局标准库副本)并指向它: ```toml [tool.basilisk] @@ -172,7 +355,10 @@ python-version = "3.12" typeshed-path = ".venv/lib/python3.12/site-packages/micropython_stdlib_stubs" ``` -由于 `micropython-stdlib-stubs` 是**部分**标准库,它未包含的模块(例如开发板上并不存在的 `tkinter`)**不会**由捆绑的 CPython 存根来兜底——自定义 typeshed 是第 3 步的规范来源,因此该导入会被报告为无法解析。对于嵌入式目标而言,这才是诚实的结果。 +由于 `micropython-stdlib-stubs` 是**部分**标准库,它未包含的模块(例如 +开发板上并不存在的 `tkinter`)**不会**由 CPython 存根来兜底——自定义 +typeshed 是第 3 步的规范来源,因此该导入会被报告为无法解析。对于嵌入式 +目标而言,这才是诚实的结果。 ### 3. 在活动项目文件中配置 @@ -184,7 +370,10 @@ typeshed-path = ".venv/lib/python3.12/site-packages/micropython_stdlib_stubs" ### 4. 确认已生效——悬停溯源 -从自定义 typeshed 解析的符号在悬停时会带有 `(custom typeshed)` 标记,区别于捆绑 typeshed 的 `(typeshed)` 标记。将鼠标悬停在导入的标准库符号上:看到 `(custom typeshed)` 即可确认覆盖已生效,且该签名来自你的目录——MicroPython 的 `os.uname` 绝不会被误报为 CPython 的。 +从自定义 typeshed 解析的符号在悬停时会带有 `(custom typeshed)` 标记, +区别于官方来源的 `(typeshed)` 标记。将鼠标悬停在导入的标准库符号上: +看到 `(custom typeshed)` 即可确认覆盖已生效,且该签名来自你的目录—— +MicroPython 的 `os.uname` 绝不会被误报为 CPython 的。 ### `typeshed-path` 与 `stub-paths` 的区别 @@ -192,73 +381,21 @@ typeshed-path = ".venv/lib/python3.12/site-packages/micropython_stdlib_stubs" | | `stub-paths`(第 1 步) | `typeshed-path`(第 3 步) | | --- | --- | --- | -| 作用 | 在搜索路径最前端*前置*额外的 `.pyi` 目录 | *整体替换*捆绑的标准库 typeshed | +| 作用 | 在搜索路径最前端*前置*额外的 `.pyi` 目录 | *整体替换*标准库 typeshed | | 范围 | 可遮蔽任意单个模块,无论标准库还是第三方 | 整个标准库的规范来源 | -| 典型用途 | 修补某个损坏的存根;为内部库提供存根 | 面向替代或分叉的标准库(MicroPython、更新的 typeshed) | +| 典型用途 | 修补某个损坏的存根;为内部库提供存根 | 面向替代或分叉的标准库(MicroPython、打过补丁的树) | | 优先级 | 更高——`stub-paths` 中的模块仍会遮蔽自定义 typeshed | 位于 `stub-paths` 之下、已安装包之上 | --- -## 规则选择与全局严重性 - -未配置时,Basilisk 启用完整的核心 PEP 规则集;带 `basilisk` 标签的扩展 -规则默认关闭。不存在 basic/standard/strict 模式,也不存在规则族开关。 -配置编辑器中的 **Strict 预设**只是一次性配方:它把每条实时规则的原生 -严重性显式写入活动配置文件,之后每条规则仍可独立调整。 - -任何非 `disabled` 的显式严重性都会启用相应的可选规则: - -```toml -[tool.basilisk.rules] -"BSK-0001" = "error" -"BSK-0011" = "warning" -"BSK-0152" = "error" -"BSK-0060" = "info" -``` - -可用值为 `"error"`、`"warning"`、`"info"` 与 `"disabled"`。标签只用于 -浏览和批量选择,不会充当隐藏开关。所有项目策略都保存在这一个活动配置 -文件中。 - ---- - -## `[tool.basilisk.per-path-overrides.""]` - -将不同的设置应用于特定路径。glob 与相对于项目根目录的文件路径匹配。 - -```toml -[tool.basilisk.per-path-overrides."legacy/**"] -# 为匹配的文件完全禁用规则 -disabled = ["returns_compatibility"] - -[tool.basilisk.per-path-overrides."tests/**"] -# 或降低规则的严重性而不是完全禁用 -rules."returns_compatibility" = "warning" -``` - -### `disabled` - -**类型:** `string[]` -**示例:** `["returns_compatibility", "BSK-0001"]` - -为匹配此 glob 的文件完全禁用的规则代码。 - -### `rules` - -**类型:** 规则代码 → 严重性的表 -**严重性:** `"error"`、`"warning"`、`"info"`、`"disabled"` -**示例:** `rules."returns_compatibility" = "warning"` - -为匹配的文件覆盖特定规则的严重性。尽可能选择降低或禁用单个规则,而不是放宽大范围的检查。 - ---- - ## 内联抑制 -使用标准的 `# type: ignore` 拼写。指定 Basilisk 规则代码可让抑制保持精确: +使用标准的 +[`# type: ignore`](https://typing.python.org/en/latest/spec/directives.html#type-ignore-comments) +拼写。指定 Basilisk 规则代码可让抑制保持精确: ```python -result: Any = get_legacy_value() # type: ignore[returns_compatibility] +result = get_legacy_value() # type: ignore[returns_compatibility] ``` 裸 ignore(以及其他检查器的代码)按 PEP 484 兼容语义抑制整行: @@ -275,7 +412,17 @@ value = legacy_call() # type: info[returns_compatibility] value = legacy_call() # type: disabled[returns_compatibility] ``` -文件级指令必须位于文件顶部并单独成行: +单独成行的指令会开启一个**块**,由匹配的 `end-` 指令关闭: + +```python +# type: disabled[imports_unresolved] +from fastmcp import FastMCP +from result import Result +# type: end-disabled[imports_unresolved] +``` + +文件级指令是单独成行的注释——`relaxed` 将整个文件中的所有错误降级为 +警告;`file-` 形式将同一效果应用于特定代码(不写代码则应用于所有规则): ```python # basilisk: relaxed @@ -283,42 +430,32 @@ value = legacy_call() # type: disabled[returns_compatibility] # basilisk: file-disabled[imports_unresolved] ``` -`suppressions` 标签下的四条审计规则默认全部关闭:`BSK-0060`(有效且 -精确)、`BSK-0061`(有效但宽泛)、`BSK-0062`(未使用)与 -`BSK-0063`(格式错误)。它们和其他规则一样可分别配置为 error、warning、 -info 或 disabled。格式错误的指令可以被审计,但绝不会真正抑制诊断。 +抑制审计是**可选的**标签规则族(`[tool.basilisk.rule-tags]` 中的 +`"suppressions"`),默认不产生任何输出。`BSK-0060`(有效且精确)、 +`BSK-0061`(有效但宽泛)、`BSK-0062`(未使用)与 `BSK-0063`(格式错误) +可分别独立配置为 error、warning、info 或 disabled。 --- -## 配置发现 - -Basilisk 按被检查的文件逐一发现配置:从该文件所在目录**向上**遍历,每一个 -带有 `[tool.basilisk]` 表的祖先 `pyproject.toml` 都会参与,且这些表会累积 -合并——同一个键在多个文件中都有设置时,**最近**的文件生效。子表未设置的键 -继续沿用祖先的值,因此嵌套的 `pyproject.toml` 只是细化根配置,绝不会将 -根配置整体清除。 - -旧版根目录 `basilisk.json` **绝不**会被读取。若该文件仍然存在,配置编辑器 -会将其报告为被忽略的遮蔽来源;请将其键翻译为 `[tool.basilisk]` 后删除该 -文件。 +## 采用债务 -如果没有任何祖先 `pyproject.toml` 带有 `[tool.basilisk]` 表,Basilisk 使用默认值:启用**核心 PEP 符合性规则集**(额外的 Basilisk 规则保持可选),`python-version = "3.12"`,检查当前目录。 +`basilisk adopt` 将某个文件夹的现有诊断记录为活动配置文件中普通的 +warning 严重性 `[tool.basilisk.rules]` 条目——没有边车文件、标记或隐藏 +状态。`basilisk unadopt` 删除这些条目;重新运行 `adopt` 会重新计算它们, +因此不再触发的规则会自动恢复到完整严重性。 --- ## 可视化配置编辑器 -VS Code 中的标签优先配置编辑器直接读取 LSP 的实时规则目录。它按来源、 -PEP 分类与策略标签浏览规则,支持逐规则及批量严重性、路径覆盖、精确的 -前后变更预览和分页出现位置。 - -严格优先采用流程由三个显式操作组成:应用 Strict 或 Maximum 预设;运行 -限定到当前根目录的安全修复;刷新后用 `WithoutSafeFix` 检查剩余债务,再 -显式选择 disabled、较低严重性或更窄的路径覆盖。抑制审计预设只会把 -`suppressions` 标签展开为普通规则条目,不会写入模式标志。 - -采用债务也保存在同一个配置文件的精确文件 `per-path-overrides` 中,并带 -`adoption = true` 来源标记;不会创建 `.basilisk/adoptions.toml` 或任何隐藏 -状态。VSIX 本身不解析或写入配置,所有操作均由可复用的 LSP API 完成。 +VS Code 中的标签优先配置编辑器从 LSP 读取实时规则目录,预览批量变更, +并展示每条规则的生效严重性及其决定位置。它的编辑是由 LSP 应用到活动 +`pyproject.toml` 的类型化变更——设置或移除规则条目、标签条目或 +typeshed 设置;对 PEP 规则请求 `disabled` 会作为错误被拒绝。扩展本身 +从不解析或写入配置文件;文件夹配置支撑编辑器的作用域定级视图。 ![Basilisk 的标签优先 VS Code 配置编辑器,展示实时规则分类和逐规则严重性控制](/assets/images/vscode-configuration-editor.png) + +请追踪权威的 +[规范](https://github.com/Nimblesite/Basilisk/blob/main/docs/specs/LSP-CONFIGURATION-EDITOR-SPEC.md) +与[实现计划](https://github.com/Nimblesite/Basilisk/blob/main/docs/plans/LSP-CONFIGURATION-EDITOR-PLAN.md)。 diff --git a/website/src/zh/docs/index.md b/website/src/zh/docs/index.md index ac499a56..99060c3f 100644 --- a/website/src/zh/docs/index.md +++ b/website/src/zh/docs/index.md @@ -47,12 +47,13 @@ Basilisk 的行为完全由**配置**决定,而默认配置恰好就是**核 比规范更严格的检查是**可选启用**的。Basilisk 还附带规范未定义的额外规则——要求每个参数和返回值都有注解、冗余注解警告、缺失 `@override` 提示、显式 `Any` 提示。在你于配置中启用之前,它们始终**关闭**。由于它们会标记规范视为有效的代码,刻意开启它们就是用严格的规范符合换取由团队自行选择的更严格标准——这是逐项目的选择,绝非默认。 -配置同样是你为需要的路径放宽规则的地方——例如在某个遗留目录中软化或禁用某条规则: +配置同样是你为需要的路径放宽规则的地方——在那个文件夹放置带 `[tool.basilisk]` 表的 `pyproject.toml`,最近作出决定的表对其下的文件胜出: ```toml -[tool.basilisk.per-path-overrides."legacy/**"] -disabled = ["returns_compatibility"] # 为遗留代码完全禁用某规则 -rules."imports_unresolved" = "warning" # 或仅降低其严重性 +# legacy/pyproject.toml +[tool.basilisk.rules] +"returns_compatibility" = "warning" # 仅为遗留代码降级 +"imports_unresolved" = "info" ``` 这让默认保持诚实——纯粹的规范符合——同时让每个团队在他们想要的地方精确地调节严格程度。 diff --git a/website/src/zh/docs/installation.md b/website/src/zh/docs/installation.md index 83978d9c..af629b77 100644 --- a/website/src/zh/docs/installation.md +++ b/website/src/zh/docs/installation.md @@ -1,8 +1,8 @@ --- layout: layouts/docs.njk title: 安装 -description: 如何安装 Basilisk——通过 Homebrew、Scoop、预构建二进制文件、VS Code 扩展、Zed 扩展或从源代码构建。 -keywords: basilisk, 安装, homebrew, scoop, rust, python类型检查器, vs code, zed +description: 如何安装 Basilisk——通过 PyPI(uv 或 pipx)、Homebrew、Scoop、预构建二进制文件、VS Code 扩展、Zed 扩展或从源代码构建。 +keywords: basilisk, 安装, pypi, pip, uv, pipx, homebrew, scoop, rust, python类型检查器, vs code, zed lang: zh --- @@ -37,6 +37,18 @@ cargo build --release 仅当您明确想覆盖 VSIX 中捆绑的二进制文件时,才需要设置 `basilisk.executablePath`、`basilisk.binaries.basilisk` 或 `basilisk.binaries.path`。 +## PyPI(uv、pipx) + +wheel 包 [`basilisk-python`](https://pypi.org/project/basilisk-python/) 捆绑的是与 Homebrew、Scoop 和 GitHub Releases 完全相同的原生二进制文件。请将其作为独立工具安装,这样 `basilisk` 命令会进入 PATH,而不会影响任何项目环境: + +```bash +uv tool install basilisk-python +# 或 +pipx install basilisk-python +``` + +安装后的命令仍然是 `basilisk`(发行版之所以命名为 `basilisk-python`,只是因为 `basilisk` 这个名字在 PyPI 上已被占用)。wheel 发布平台:Linux(x86_64、aarch64)、macOS(Apple Silicon)和 Windows(x64、arm64)。wheel 中不含任何 Python 代码——它就是同一个独立的 Rust 二进制文件,因此适用于任何 CPython 或 PyPy 3.x。 + ## Homebrew (macOS、Linux) ```bash diff --git a/website/src/zh/docs/migration.md b/website/src/zh/docs/migration.md index 19f45094..9047aba0 100644 --- a/website/src/zh/docs/migration.md +++ b/website/src/zh/docs/migration.md @@ -43,15 +43,16 @@ exclude = [ 核心 PEP 规则无需开关。Basilisk 扩展规则通过明确的逐规则严重性启用: ```toml +[tool.basilisk.rule-tags] +"basilisk" = "error" # 一行启用所有可选自有规则 + [tool.basilisk.rules] -"BSK-0001" = "error" -"BSK-0025" = "error" -"BSK-0011" = "warning" -"BSK-0152" = "error" +"BSK-0011" = "warning" # 再对个别规则作出高于标签条目的定级 ``` 规则按实时的来源、PEP 类别和描述性标签组织。Basilisk 没有 -basic/standard/strict 运行模式或规则族开关;策略由配置文件中的逐规则严重性组成。 +basic/standard/strict 运行模式;策略就是配置文件中持久化的规则条目与 +标签条目。参见[规则:两个扁平映射](/zh/docs/configuration/#规则-两个扁平映射)。 ## 3. 先应用安全修复 @@ -76,16 +77,20 @@ basilisk fix src/ tests/ 支持 `error`、`warning`、`info` 和 `disabled`。显式的非 disabled 严重性 也会启用一个默认关闭的 Basilisk 规则。 -将遗留债务限制在路径中: +将遗留债务限制在路径中:在那个文件夹放置带 `[tool.basilisk]` 表的 +`pyproject.toml`——最近作出决定的表按规则胜出,树的其余部分保持严格定级: ```toml -[tool.basilisk.per-path-overrides."legacy/**"] -rules."returns_compatibility" = "warning" -rules."imports_unresolved" = "info" +# legacy/pyproject.toml +[tool.basilisk.rules] +"returns_compatibility" = "warning" +"imports_unresolved" = "info" ``` -项目级 `disabled` 也会隐藏以后新增的问题;若债务只存在于旧文件, -路径或精确文件例外更安全。 +规则配置中没有 glob 路径模式或逐路径覆盖表——作用域策略始终是放在 +对应文件夹里的配置文件。项目级 `disabled` 也会隐藏以后新增的问题 +(且对 PEP 规则无效——它们只能定级);若债务只存在于旧代码, +`basilisk adopt` 是更安全的工具。 ## 5. 保留并审计内联忽略 @@ -114,7 +119,7 @@ error、warning、info 或 disabled。 | `stubPath` | `[tool.basilisk].stub-paths` | | `typeshedPath` | `[tool.basilisk].typeshed-path` | | `report…` 严重性 | `[tool.basilisk.rules]."RULE_CODE"` | -| 执行环境例外 | 语义相符时使用 `per-path-overrides` | +| 执行环境例外 | 在对应文件夹放置带 `[tool.basilisk]` 表的 `pyproject.toml` | 不要机械映射 `typeCheckingMode`;Basilisk 将策略保存为显式规则严重性, 而不是模式。 @@ -127,7 +132,7 @@ error、warning、info 或 disabled。 | `exclude` | `[tool.basilisk].exclude` | | `mypy_path` | 包含存根时使用 `[tool.basilisk].stub-paths` | | `custom_typeshed_dir` | `[tool.basilisk].typeshed-path` | -| 每模块放宽 | 适用时使用每路径覆盖 | +| 每模块放宽 | 例外基于源码路径时,使用文件夹作用域的 `pyproject.toml` | | `# type: ignore[code]` | 保留语法并换成 Basilisk 规则码 | mypy 插件不能直接加载到 Basilisk。对于框架特有债务,应使用有针对性的 @@ -138,16 +143,15 @@ mypy 插件不能直接加载到 Basilisk。对于框架特有债务,应使用 VS Code 配置编辑器采用标签优先界面,并由可复用 LSP API 驱动: 1. 按权威标签浏览实时规则目录; -2. 预览 LSP 提供的 **Strict preset**,把所有规则按各自原生严重性启用; -3. 先运行 Safe 修复; -4. 按标签、规则、文件和可修复性审查剩余债务; -5. 只降低选中的规则,或为受影响文件记录精确例外; -6. 检查准确影响后,通过版本令牌应用; -7. 持续查找采用例外和可选的忽略审计诊断。 - -Preset 是一次性的配置配方,不是运行模式。应用 Strict 后,LSP 会把展开 -后的逐规则严重性写入当前有效配置文件。精确文件采用同样写入该文件的 -`per-path-overrides`,不会创建 `.basilisk/adoptions.toml` 或隐藏状态。 +2. 用一条 `rule-tags` 条目(例如 `"basilisk" = "error"`)启用整组规则, + 并在应用前预览效果; +3. 先运行 Safe 修复(独立的根作用域 LSP 操作); +4. 按标签和规则审查剩余债务; +5. 显式降低选中的规则,或用 `basilisk adopt` 记录债务; +6. 持续跟踪例外与可选的抑制审计诊断,直至清零。 + +采用债务保存为同一个活动配置文件中普通的 warning 严重性规则条目, +不会创建第二个配置文件或持久模式。 参见权威的 [规范](https://github.com/Nimblesite/Basilisk/blob/main/docs/specs/LSP-CONFIGURATION-EDITOR-SPEC.md) diff --git a/website/src/zh/docs/quick-start.md b/website/src/zh/docs/quick-start.md index 095b184e..14972cff 100644 --- a/website/src/zh/docs/quick-start.md +++ b/website/src/zh/docs/quick-start.md @@ -154,8 +154,9 @@ Found 2 diagnostics (0 errors). 提升为 `"error"`,您的项目就达到了完全严格。 在 VS Code 中无需手动编辑:从命令面板运行 **Basilisk: Open Configuration -Editor**。它展示每条规则的实时严重级别,应用前可预览更改,其 **Strict -预设**一键启用完整规则目录。完整模式请参阅[配置参考](/zh/docs/configuration/)。 +Editor**。它展示每条规则的实时严重级别,应用前可预览更改,其标签操作用 +一条 `rule-tags` 配置行(例如 `"basilisk" = "error"`)启用整组规则。 +完整模式请参阅[配置参考](/zh/docs/configuration/)。 ![Basilisk 的标签优先 VS Code 配置编辑器,显示实时规则分面和每条规则的严重级别控件](/assets/images/vscode-configuration-editor.png)