This is an automated email from the ASF dual-hosted git repository.
sbp pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-releases.git
The following commit(s) were added to refs/heads/main by this push:
new 513a192 Add a script to fix function order
513a192 is described below
commit 513a1922d18d177c9d6b4dd9e9fdbce9a1a16f9f
Author: Sean B. Palmer <[email protected]>
AuthorDate: Tue Dec 2 14:46:16 2025 +0000
Add a script to fix function order
---
scripts/fix_order.py | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++
scripts/fix_order.sh | 31 +++++++++++++
2 files changed, 159 insertions(+)
diff --git a/scripts/fix_order.py b/scripts/fix_order.py
new file mode 100755
index 0000000..13d3f27
--- /dev/null
+++ b/scripts/fix_order.py
@@ -0,0 +1,128 @@
+#!/usr/bin/env python3
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+import pathlib
+import sys
+
+
+def main() -> None:
+ if len(sys.argv) != 2:
+ print("Usage: fix_order.py <filename>", file=sys.stderr)
+ sys.exit(2)
+
+ path = pathlib.Path(sys.argv[1])
+ blocks = _parse_blocks(path.read_text(encoding="utf-8"))
+ nonfunc, func = [], []
+ seen_func = False
+ main_guard: list[str] | None = None
+
+ if blocks and _is_main_guard(blocks[-1][1]):
+ main_guard = blocks[-1][1]
+ blocks = blocks[:-1]
+
+ for lineno, lines in blocks:
+ count = _count_defs(lines)
+ if count > 1:
+ sys.exit(f"{path}:{lineno}: multiple function definitions in
block")
+ if _is_func(lines):
+ seen_func = True
+ func.append((lineno, lines))
+ elif seen_func:
+ sys.exit(f"{path}:{lineno}: non-function block after function
block")
+ else:
+ nonfunc.append((lineno, lines))
+
+ func.sort(key=_sort_key)
+ nonfunc_text = "".join("".join(b) for _, b in nonfunc)
+ func_parts = [_normalise(b) for _, b in func]
+ func_text = "\n\n".join(func_parts)
+ if nonfunc_text and func_text:
+ output = nonfunc_text.rstrip("\n") + "\n\n\n" + func_text
+ else:
+ output = nonfunc_text + func_text
+ if main_guard:
+ if output:
+ output = output.rstrip("\n") + "\n\n\n" + _normalise(main_guard)
+ else:
+ output = _normalise(main_guard)
+ print(output, end="")
+
+
+def _count_defs(block: list[str]) -> int:
+ return sum(1 for line in block if line.startswith(("def ", "async def ")))
+
+
+def _is_func(block: list[str]) -> bool:
+ for line in block:
+ if not line.strip():
+ continue
+ if line.lstrip() != line:
+ break
+ if line.startswith(("def ", "async def ")):
+ return True
+ return False
+
+
+def _is_main_guard(block: list[str]) -> bool:
+ for line in block:
+ if line.strip():
+ return line.startswith("if __name__")
+ return False
+
+
+def _normalise(block: list[str]) -> str:
+ while block and not block[-1].strip():
+ block = block[:-1]
+ return "".join(block).rstrip("\n") + "\n"
+
+
+def _parse_blocks(text: str) -> list[tuple[int, list[str]]]:
+ blocks: list[tuple[int, list[str]]] = []
+ cur: list[str] = []
+ start = 1
+ after_blank = False
+ for lineno, line in enumerate(text.splitlines(keepends=True), 1):
+ is_blank = not line.strip()
+ is_unindented = line.lstrip() == line
+ if is_unindented and (not is_blank) and after_blank and cur:
+ blocks.append((start, cur))
+ cur = []
+ start = lineno
+ cur.append(line)
+ after_blank = is_blank
+ if cur:
+ blocks.append((start, cur))
+ return blocks
+
+
+def _sort_key(block: tuple[int, list[str]]) -> str:
+ for line in block[1]:
+ if line.startswith("def "):
+ return _toggle(line[4:].split("(")[0])
+ if line.startswith("async def "):
+ return _toggle(line[10:].split("(")[0])
+ return ""
+
+
+def _toggle(name: str) -> str:
+ return name[1:] if name.startswith("_") else "_" + name
+
+
+if __name__ == "__main__":
+ main()
diff --git a/scripts/fix_order.sh b/scripts/fix_order.sh
new file mode 100755
index 0000000..b065c95
--- /dev/null
+++ b/scripts/fix_order.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+set -eu
+
+if [ $# -ne 1 ]
+then
+ echo "Usage: fix_order.sh <filename>" >&2
+ exit 2
+fi
+
+file="$1"
+tmp="$$.tmp.py"
+backup="$HOME/.fix_order.backup.py"
+script_dir="$(dirname "$0")"
+
+# TODO: Use uv here?
+python3 "$script_dir/fix_order.py" "$file" > "$tmp"
+status=$?
+if [ $status -ne 0 ]
+then
+ rm -f "$tmp"
+ exit $status
+fi
+
+if cmp -s "$file" "$tmp"
+then
+ rm -f "$tmp"
+else
+ cp "$file" "$backup"
+ diff -u "$file" "$tmp"
+ mv "$tmp" "$file"
+fi
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]