formatting.sh 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. #!/usr/bin/env bash
  2. # YAPF formatter, adapted from ray and skypilot.
  3. #
  4. # Usage:
  5. # # Do work and commit your work.
  6. # # Format files that differ from origin/main.
  7. # bash formatting.sh
  8. # # Commit changed files with message 'Run yapf and ruff'
  9. #
  10. #
  11. # YAPF + Clang formatter (if installed). This script formats all changed files from the last mergebase.
  12. # You are encouraged to run this locally before pushing changes for review.
  13. # Cause the script to exit if a single command fails
  14. set -eo pipefail
  15. # this stops git rev-parse from failing if we run this from the .git directory
  16. builtin cd "$(dirname "${BASH_SOURCE:-$0}")"
  17. ROOT="$(git rev-parse --show-toplevel)"
  18. builtin cd "$ROOT" || exit 1
  19. YAPF_VERSION=$(yapf --version | awk '{print $2}')
  20. RUFF_VERSION=$(ruff --version | awk '{print $2}')
  21. MYPY_VERSION=$(mypy --version | awk '{print $2}')
  22. CODESPELL_VERSION=$(codespell --version)
  23. CLANGFORMAT_VERSION=$(clang-format --version | awk '{print $3}')
  24. # # params: tool name, tool version, required version
  25. tool_version_check() {
  26. if [[ $2 != $3 ]]; then
  27. echo "Wrong $1 version installed: $3 is required, not $2."
  28. exit 1
  29. fi
  30. }
  31. tool_version_check "yapf" $YAPF_VERSION "$(grep yapf requirements-dev.txt | cut -d'=' -f3)"
  32. tool_version_check "ruff" $RUFF_VERSION "$(grep "ruff==" requirements-dev.txt | cut -d'=' -f3)"
  33. tool_version_check "mypy" "$MYPY_VERSION" "$(grep mypy requirements-dev.txt | cut -d'=' -f3)"
  34. tool_version_check "codespell" "$CODESPELL_VERSION" "$(grep codespell requirements-dev.txt | cut -d'=' -f3)"
  35. tool_version_check "clang-format" "$CLANGFORMAT_VERSION" "$(grep clang-format requirements-dev.txt | cut -d'=' -f3)"
  36. YAPF_FLAGS=(
  37. '--recursive'
  38. '--parallel'
  39. )
  40. YAPF_EXCLUDES=(
  41. '--exclude' 'build/**'
  42. )
  43. # Format specified files
  44. format() {
  45. yapf --in-place "${YAPF_FLAGS[@]}" "$@"
  46. }
  47. # Format files that differ from main branch. Ignores dirs that are not slated
  48. # for autoformat yet.
  49. format_changed() {
  50. # The `if` guard ensures that the list of filenames is not empty, which
  51. # could cause yapf to receive 0 positional arguments, making it hang
  52. # waiting for STDIN.
  53. #
  54. # `diff-filter=ACM` and $MERGEBASE is to ensure we only format files that
  55. # exist on both branches.
  56. MERGEBASE="$(git merge-base origin/main HEAD)"
  57. if ! git diff --diff-filter=ACM --quiet --exit-code "$MERGEBASE" -- '*.py' '*.pyi' &>/dev/null; then
  58. git diff --name-only --diff-filter=ACM "$MERGEBASE" -- '*.py' '*.pyi' | xargs -P 5 \
  59. yapf --in-place "${YAPF_EXCLUDES[@]}" "${YAPF_FLAGS[@]}"
  60. fi
  61. }
  62. # Format all files
  63. format_all() {
  64. yapf --in-place "${YAPF_FLAGS[@]}" "${YAPF_EXCLUDES[@]}" .
  65. }
  66. ## This flag formats individual files. --files *must* be the first command line
  67. ## arg to use this option.
  68. if [[ "$1" == '--files' ]]; then
  69. format "${@:2}"
  70. # If `--all` is passed, then any further arguments are ignored and the
  71. # entire python directory is formatted.
  72. elif [[ "$1" == '--all' ]]; then
  73. format_all
  74. else
  75. # Format only the files that changed in last commit.
  76. format_changed
  77. fi
  78. echo 'Aphrodite yapf: Done'
  79. CODESPELL_EXCLUDES=(
  80. '--skip' '*docs/source/_build/**'
  81. )
  82. # check spelling of specified files
  83. spell_check() {
  84. codespell "$@"
  85. }
  86. spell_check_all(){
  87. codespell --toml pyproject.toml "${CODESPELL_EXCLUDES[@]}"
  88. }
  89. # Spelling check of files that differ from main branch.
  90. spell_check_changed() {
  91. # The `if` guard ensures that the list of filenames is not empty, which
  92. # could cause ruff to receive 0 positional arguments, making it hang
  93. # waiting for STDIN.
  94. #
  95. # `diff-filter=ACM` and $MERGEBASE is to ensure we only lint files that
  96. # exist on both branches.
  97. MERGEBASE="$(git merge-base origin/main HEAD)"
  98. if ! git diff --diff-filter=ACM --quiet --exit-code "$MERGEBASE" -- '*.py' '*.pyi' &>/dev/null; then
  99. git diff --name-only --diff-filter=ACM "$MERGEBASE" -- '*.py' '*.pyi' | xargs \
  100. codespell "${CODESPELL_EXCLUDES[@]}"
  101. fi
  102. }
  103. # Run Codespell
  104. ## This flag runs spell check of individual files. --files *must* be the first command line
  105. ## arg to use this option.
  106. if [[ "$1" == '--files' ]]; then
  107. spell_check "${@:2}"
  108. # If `--all` is passed, then any further arguments are ignored and the
  109. # entire python directory is linted.
  110. elif [[ "$1" == '--all' ]]; then
  111. spell_check_all
  112. else
  113. # Check spelling only of the files that changed in last commit.
  114. spell_check_changed
  115. fi
  116. echo 'Aphrodite codespell: Done'
  117. # Lint specified files
  118. lint() {
  119. ruff "$@"
  120. }
  121. # Lint files that differ from main branch. Ignores dirs that are not slated
  122. # for autolint yet.
  123. lint_changed() {
  124. # The `if` guard ensures that the list of filenames is not empty, which
  125. # could cause ruff to receive 0 positional arguments, making it hang
  126. # waiting for STDIN.
  127. #
  128. # `diff-filter=ACM` and $MERGEBASE is to ensure we only lint files that
  129. # exist on both branches.
  130. MERGEBASE="$(git merge-base origin/main HEAD)"
  131. if ! git diff --diff-filter=ACM --quiet --exit-code "$MERGEBASE" -- '*.py' '*.pyi' &>/dev/null; then
  132. git diff --name-only --diff-filter=ACM "$MERGEBASE" -- '*.py' '*.pyi' | xargs \
  133. ruff
  134. fi
  135. }
  136. # Run Ruff
  137. echo 'Aphrodite ruff:'
  138. ### This flag lints individual files. --files *must* be the first command line
  139. ### arg to use this option.
  140. if [[ "$1" == '--files' ]]; then
  141. lint "${@:2}"
  142. # If `--all` is passed, then any further arguments are ignored and the
  143. # entire python directory is linted.
  144. elif [[ "$1" == '--all' ]]; then
  145. lint aphrodite tests
  146. else
  147. # Format only the files that changed in last commit.
  148. lint_changed
  149. fi
  150. # Clang-format section
  151. # Exclude some files for formatting because they are vendored
  152. # NOTE: Keep up to date with .github/workflows/clang-format.yml
  153. CLANG_FORMAT_EXCLUDES=(
  154. 'kernels/moe/topk_softmax_kernels.cu'
  155. 'kerneks/punica/bgmv/bgmv_bf16_bf16_bf16.cu'
  156. 'kerneks/punica/bgmv/bgmv_config.h'
  157. 'kernels/punica/bgmv/bgmv_impl.cuh'
  158. 'kernels/punica/bgmv/vec_dtypes.cuh'
  159. 'kernels/punica/punica_ops.cu'
  160. 'kernels/punica/type_convert.h'
  161. )
  162. # Format specified files with clang-format
  163. clang_format() {
  164. clang-format -i "$@"
  165. }
  166. # Format files that differ from main branch with clang-format.
  167. clang_format_changed() {
  168. # The `if` guard ensures that the list of filenames is not empty, which
  169. # could cause clang-format to receive 0 positional arguments, making it hang
  170. # waiting for STDIN.
  171. #
  172. # `diff-filter=ACM` and $MERGEBASE is to ensure we only format files that
  173. # exist on both branches.
  174. MERGEBASE="$(git merge-base origin/main HEAD)"
  175. # Get the list of changed files, excluding the specified ones
  176. changed_files=$(git diff --name-only --diff-filter=ACM "$MERGEBASE" -- '*.h' '*.cpp' '*.cu' '*.cuh' | grep -vFf <(printf "%s\n" "${CLANG_FORMAT_EXCLUDES[@]}"))
  177. if [ -n "$changed_files" ]; then
  178. echo "$changed_files" | xargs -P 5 clang-format -i
  179. fi
  180. }
  181. # Format all files with clang-format
  182. clang_format_all() {
  183. find kernels/ \( -name '*.h' -o -name '*.cpp' -o -name '*.cu' -o -name '*.cuh' \) -print \
  184. | grep -vFf <(printf "%s\n" "${CLANG_FORMAT_EXCLUDES[@]}") \
  185. | xargs clang-format -i
  186. }
  187. # Run clang-format
  188. if [[ "$1" == '--files' ]]; then
  189. clang_format "${@:2}"
  190. elif [[ "$1" == '--all' ]]; then
  191. clang_format_all
  192. else
  193. clang_format_changed
  194. fi
  195. echo 'Aphrodite clang-format: Done'
  196. if ! git diff --quiet &>/dev/null; then
  197. echo 'Reformatted files. Please review and stage the changes.'
  198. echo 'Changes not staged for commit:'
  199. echo
  200. git --no-pager diff --name-only
  201. exit 1
  202. fi