openai/openai-python

Public

mirrored from https://github.com/openai/openai-pythonAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.18.0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

bin/ruffen-docs.py

167lines · modeblame

e1b60b22Stainless Bot2 years ago1# fork of https://github.com/asottile/blacken-docs adapted for ruff
08b8179aDavid Schnurr2 years ago2from __future__ import annotations
3
4import re
e1b60b22Stainless Bot2 years ago5import sys
08b8179aDavid Schnurr2 years ago6import argparse
7import textwrap
8import contextlib
e1b60b22Stainless Bot2 years ago9import subprocess
08b8179aDavid Schnurr2 years ago10from typing import Match, Optional, Sequence, Generator, NamedTuple, cast
11
12MD_RE = re.compile(
13r"(?P<before>^(?P<indent> *)```\s*python\n)" r"(?P<code>.*?)" r"(?P<after>^(?P=indent)```\s*$)",
14re.DOTALL | re.MULTILINE,
15)
16MD_PYCON_RE = re.compile(
17r"(?P<before>^(?P<indent> *)```\s*pycon\n)" r"(?P<code>.*?)" r"(?P<after>^(?P=indent)```.*$)",
18re.DOTALL | re.MULTILINE,
19)
20PYCON_PREFIX = ">>> "
21PYCON_CONTINUATION_PREFIX = "..."
22PYCON_CONTINUATION_RE = re.compile(
23rf"^{re.escape(PYCON_CONTINUATION_PREFIX)}( |$)",
24)
e1b60b22Stainless Bot2 years ago25DEFAULT_LINE_LENGTH = 100
08b8179aDavid Schnurr2 years ago26
27
28class CodeBlockError(NamedTuple):
29offset: int
30exc: Exception
31
32
33def format_str(
34src: str,
35) -> tuple[str, Sequence[CodeBlockError]]:
36errors: list[CodeBlockError] = []
37
38@contextlib.contextmanager
39def _collect_error(match: Match[str]) -> Generator[None, None, None]:
40try:
41yield
42except Exception as e:
43errors.append(CodeBlockError(match.start(), e))
44
45def _md_match(match: Match[str]) -> str:
46code = textwrap.dedent(match["code"])
47with _collect_error(match):
e1b60b22Stainless Bot2 years ago48code = format_code_block(code)
08b8179aDavid Schnurr2 years ago49code = textwrap.indent(code, match["indent"])
50return f'{match["before"]}{code}{match["after"]}'
51
52def _pycon_match(match: Match[str]) -> str:
53code = ""
54fragment = cast(Optional[str], None)
55
56def finish_fragment() -> None:
57nonlocal code
58nonlocal fragment
59
60if fragment is not None:
61with _collect_error(match):
e1b60b22Stainless Bot2 years ago62fragment = format_code_block(fragment)
08b8179aDavid Schnurr2 years ago63fragment_lines = fragment.splitlines()
64code += f"{PYCON_PREFIX}{fragment_lines[0]}\n"
65for line in fragment_lines[1:]:
66# Skip blank lines to handle Black adding a blank above
67# functions within blocks. A blank line would end the REPL
68# continuation prompt.
69#
70# >>> if True:
71# ... def f():
72# ... pass
73# ...
74if line:
75code += f"{PYCON_CONTINUATION_PREFIX} {line}\n"
76if fragment_lines[-1].startswith(" "):
77code += f"{PYCON_CONTINUATION_PREFIX}\n"
78fragment = None
79
80indentation = None
81for line in match["code"].splitlines():
82orig_line, line = line, line.lstrip()
83if indentation is None and line:
84indentation = len(orig_line) - len(line)
85continuation_match = PYCON_CONTINUATION_RE.match(line)
86if continuation_match and fragment is not None:
87fragment += line[continuation_match.end() :] + "\n"
88else:
89finish_fragment()
90if line.startswith(PYCON_PREFIX):
91fragment = line[len(PYCON_PREFIX) :] + "\n"
92else:
93code += orig_line[indentation:] + "\n"
94finish_fragment()
95return code
96
97def _md_pycon_match(match: Match[str]) -> str:
98code = _pycon_match(match)
99code = textwrap.indent(code, match["indent"])
100return f'{match["before"]}{code}{match["after"]}'
101
102src = MD_RE.sub(_md_match, src)
103src = MD_PYCON_RE.sub(_md_pycon_match, src)
104return src, errors
105
106
e1b60b22Stainless Bot2 years ago107def format_code_block(code: str) -> str:
108return subprocess.check_output(
109[
110sys.executable,
111"-m",
112"ruff",
113"format",
114"--stdin-filename=script.py",
115f"--line-length={DEFAULT_LINE_LENGTH}",
116],
117encoding="utf-8",
118input=code,
119)
120
121
08b8179aDavid Schnurr2 years ago122def format_file(
123filename: str,
124skip_errors: bool,
125) -> int:
126with open(filename, encoding="UTF-8") as f:
127contents = f.read()
e1b60b22Stainless Bot2 years ago128new_contents, errors = format_str(contents)
08b8179aDavid Schnurr2 years ago129for error in errors:
130lineno = contents[: error.offset].count("\n") + 1
131print(f"{filename}:{lineno}: code block parse error {error.exc}")
132if errors and not skip_errors:
133return 1
134if contents != new_contents:
135print(f"{filename}: Rewriting...")
136with open(filename, "w", encoding="UTF-8") as f:
137f.write(new_contents)
138return 0
139else:
140return 0
141
142
143def main(argv: Sequence[str] | None = None) -> int:
144parser = argparse.ArgumentParser()
145parser.add_argument(
146"-l",
147"--line-length",
148type=int,
149default=DEFAULT_LINE_LENGTH,
150)
151parser.add_argument(
152"-S",
153"--skip-string-normalization",
154action="store_true",
155)
156parser.add_argument("-E", "--skip-errors", action="store_true")
157parser.add_argument("filenames", nargs="*")
158args = parser.parse_args(argv)
159
160retv = 0
161for filename in args.filenames:
e1b60b22Stainless Bot2 years ago162retv |= format_file(filename, skip_errors=args.skip_errors)
08b8179aDavid Schnurr2 years ago163return retv
164
165
166if __name__ == "__main__":
167raise SystemExit(main())