Skip to content

Commit 40153dd

Browse files
committed
chore: add a utility to generate a schema file
Signed-off-by: Henry Schreiner <[email protected]>
1 parent e9a813c commit 40153dd

File tree

2 files changed

+143
-19
lines changed

2 files changed

+143
-19
lines changed

docs/source/config_file.rst

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -745,11 +745,11 @@ section of the command line docs.
745745

746746
.. confval:: extra_checks
747747

748-
:type: boolean
749-
:default: False
748+
:type: boolean
749+
:default: False
750750

751-
This flag enables additional checks that are technically correct but may be impractical.
752-
See :option:`mypy --extra-checks` for more info.
751+
This flag enables additional checks that are technically correct but may be impractical.
752+
See :option:`mypy --extra-checks` for more info.
753753

754754
.. confval:: implicit_reexport
755755

@@ -773,31 +773,31 @@ section of the command line docs.
773773
774774
.. confval:: strict_equality
775775

776-
:type: boolean
777-
:default: False
776+
:type: boolean
777+
:default: False
778778

779-
Prohibit equality checks, identity checks, and container checks between
780-
non-overlapping types.
779+
Prohibit equality checks, identity checks, and container checks between
780+
non-overlapping types.
781781

782782
.. confval:: strict_bytes
783783

784-
:type: boolean
785-
:default: False
784+
:type: boolean
785+
:default: False
786786

787-
Disable treating ``bytearray`` and ``memoryview`` as subtypes of ``bytes``.
788-
This will be enabled by default in *mypy 2.0*.
787+
Disable treating ``bytearray`` and ``memoryview`` as subtypes of ``bytes``.
788+
This will be enabled by default in *mypy 2.0*.
789789

790790
.. confval:: strict
791791

792-
:type: boolean
793-
:default: False
792+
:type: boolean
793+
:default: False
794794

795-
Enable all optional error checking flags. You can see the list of
796-
flags enabled by strict mode in the full :option:`mypy --help`
797-
output.
795+
Enable all optional error checking flags. You can see the list of
796+
flags enabled by strict mode in the full :option:`mypy --help`
797+
output.
798798

799-
Note: the exact list of flags enabled by :confval:`strict` may
800-
change over time.
799+
Note: the exact list of flags enabled by :confval:`strict` may
800+
change over time.
801801

802802

803803
Configuring error messages

misc/make_schema.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
from __future__ import annotations
2+
3+
import dataclasses
4+
import json
5+
import re
6+
import textwrap
7+
from collections.abc import Generator, Sequence
8+
from pathlib import Path
9+
from typing import Any
10+
11+
DIR = Path(__file__).parent.resolve()
12+
13+
14+
@dataclasses.dataclass(frozen=True)
15+
class ConfigOpt:
16+
name: str
17+
type_str: str
18+
is_global: bool
19+
description: str
20+
default: str | None
21+
22+
def define(self) -> dict[str, str]:
23+
retval: dict[str, Any] = {"description": self.description}
24+
if self.default is not None:
25+
match self.type_str:
26+
case "boolean":
27+
retval["default"] = {"true": True, "false": False}[self.default.lower()]
28+
case "integer":
29+
retval["default"] = int(self.default)
30+
case "string":
31+
retval["default"] = self.default.strip("`")
32+
case _:
33+
msg = f"Default not suppored for {self.type_str}"
34+
raise RuntimeError(msg)
35+
match self.type_str:
36+
case "boolean" | "integer" | "string":
37+
retval["type"] = self.type_str
38+
case "comma-separated list of strings" | "regular expression":
39+
retval["oneOf"] = [
40+
{"type": "string"},
41+
{"type": "array", "items": {"type": "string"}},
42+
]
43+
case _:
44+
msg = f"{self.type_str} not supported for type"
45+
raise RuntimeError(msg)
46+
47+
return retval
48+
49+
50+
def parse_rst_docs(txt: str) -> Generator[ConfigOpt, None, None]:
51+
for match in re.finditer(r".. confval:: ([^\s]*)\n\n((?: .*\n|\n)*)", txt):
52+
name, body = match.groups()
53+
body = textwrap.dedent(body)
54+
body_match = re.match(r":type: (.*?)\n(?::default: (.*?)\n)?(.*)$", body, re.DOTALL)
55+
assert body_match is not None, f"{name} missing type and default!\n{body!r}"
56+
type_str, default, inner = body_match.groups()
57+
is_global = "only be set in the global section" in body
58+
description = inner.strip().split("\n\n")[0].replace("\n", " ")
59+
# Patches
60+
if name == "mypy_path":
61+
type_str = "comma-separated list of strings"
62+
yield ConfigOpt(
63+
name=name,
64+
type_str=type_str,
65+
is_global=is_global,
66+
description=description,
67+
default=default,
68+
)
69+
70+
71+
def make_schema(opts: Sequence[ConfigOpt]) -> dict[str, Any]:
72+
definitions = {s.name: s.define() for s in opts}
73+
overrides = {s.name: {"$ref": f"#/properties/{s.name}"} for s in opts if not s.is_global}
74+
module = {
75+
"oneOf": [
76+
{"type": "string"},
77+
{"type": "array", "items": {"type": "string"}, "minItems": 1},
78+
]
79+
}
80+
81+
# Improve some fields
82+
definitions["follow_imports"]["enum"] = ["normal", "silent", "skip", "error"]
83+
84+
# Undocumented fields
85+
definitions["show_error_codes"] = {
86+
"type": "boolean",
87+
"default": True,
88+
"description": "DEPRECATED and UNDOCUMENTED: Now defaults to true, use `hide_error_codes` if you need to disable error codes instead.",
89+
"deprecated": True,
90+
}
91+
definitions.setdefault(
92+
"show_error_code_links",
93+
{
94+
"type": "boolean",
95+
"default": False,
96+
"description": "UNDOCUMENTED: show links for error codes.",
97+
},
98+
)
99+
100+
return {
101+
"$schema": "http://json-schema.org/draft-07/schema#",
102+
"$id": "https://json.schemastore.org/partial-mypy.json",
103+
"additionalProperties": False,
104+
"type": "object",
105+
"properties": {
106+
**definitions,
107+
"overrides": {
108+
"type": "array",
109+
"items": {
110+
"type": "object",
111+
"additionalProperties": False,
112+
"required": ["module"],
113+
"minProperties": 2,
114+
"properties": {"module": module, **overrides},
115+
},
116+
},
117+
},
118+
}
119+
120+
121+
if __name__ == "__main__":
122+
filepath = DIR.parent / "docs/source/config_file.rst"
123+
opts = parse_rst_docs(filepath.read_text())
124+
print(json.dumps(make_schema(list(opts)), indent=2))

0 commit comments

Comments
 (0)