Skip to content

Instantly share code, notes, and snippets.

@Cynnexis
Last active April 11, 2022 08:34
Show Gist options
  • Select an option

  • Save Cynnexis/cd7fdc7b911ac39b623a3a62105e7d45 to your computer and use it in GitHub Desktop.

Select an option

Save Cynnexis/cd7fdc7b911ac39b623a3a62105e7d45 to your computer and use it in GitHub Desktop.
Git hook pre-commit to run YAPF on Python code

pre-commit yapf

Installation

One-time installation

Put the pre-commit file under .git/hooks/ of your Python project, and make sure it is well-formatted (LF) and it has the correct permissions.

The following steps will get you the pre-commit hook in your project:

  1. cd path/to/your/project
  2. curl -fsSL "https://gist.githubusercontent.com/Cynnexis/cd7fdc7b911ac39b623a3a62105e7d45/raw/pre-commit" -o .git/hooks/pre-commit
  3. If yapf is not in your PATH, edit .git/hooks/pre-commit and specify the absolute path to your YAPF executable at line 12.
  4. chmod 776 .git/hooks/pre-commit
  5. If you use Windows: dos2unix .git/hooks/pre-commit

Makefile

If you want to automate its creation from a Makefile, use the following rule:

SHELL := /bin/bash
.ONESHELL:
.PHONY: help configure-git configure

# [...]

help:
	# [...]
	@echo "  configure         - Configure the project folder."
	@echo "  configure-git     - Configure the project folder for git usage. Use 'configure' for global configuration of the project."
	# [...]

.git/hooks/pre-commit:
	curl -fsSL "https://gist.githubusercontent.com/Cynnexis/cd7fdc7b911ac39b623a3a62105e7d45/raw/pre-commit" -o ".git/hooks/pre-commit"
	@if command -v "dos2unix" > /dev/null 2>&1; then \
		dos2unix ".git/hooks/pre-commit"; \
	else \
		echo "dos2unix not found. If you are on Windows, you may consider installing it."; \
	fi

configure-git: .git/hooks/pre-commit

configure: configure-git

# [...]

VS Code

If you use Visual Studio Code, you can create a task for YAPF:

.vscode/tasks.json:

{
	"version": "2.0.0",
	"tasks": [
		{
			"label": "YAPF",
			"type": "shell",
			"group": "none",
			"command": "yapf",
			"args": [
				"-i",
				"-r",
				"${workspaceFolder}"
			],
			"presentation": {
				"reveal": "silent"
			},
			"problemMatcher": []
		}
	]
}

To bind this task to a keyboard shortcut, add the following JSON object to your global keybindings.json:

keybindings.json:

{
	"key": "ctrl+shift+y",
	"command": "workbench.action.tasks.runTask",
	"args": "YAPF",
	"when": "workbenchState == workspace && editorLangId == python"
}
#!/bin/bash
# This pre-commit hook run YAPF on your code. If the code is not well-formatted,
# it will format it automatically, and then abort the commit (so you can review
# the changes). By default, this pre-commit assume that YAPF is in $PATH. If it
# is not, please put the absolute path to yapf at line 12. On Windows, you can
# use an absolute path to your path.exe formatted as POSIX. For instance:
# "/c/Program Files/Anaconda3/envs/Commity/Scripts/yapf.exe" (note that the
# driver name is in lower case, and that the back-slashes are replaced with
# slashes).
# PUT MANUALLY YOUR ALIAS HERE
if [[ ! $(command -v "yapf" > /dev/null 2>&1) ]]; then
print_error() {
echo -e "Couldn't find 'yapf' in your system. Please edit '.git/hooks/pre-commit' to manually add:\n"
echo -e "alias yapf='path/to/yapf'\n"
echo "This line can be added at the line 12 of the file."
echo "If you are using a WSL, you might need to add before your path with 'cmd.exe /c '."
exit 1
}
YAPF="$(find / -name yapf -type f -print -quit)"
if $YAPF -v > /dev/null 2>&1; then
echo "Found YAPF: $YAPF"
else
echo "Invalid YAPF: $YAPF"
$YAPF -v
print_error
fi
fi
# Detect the added/changed files
mapfile -t PYTHON_DIRS < <(git diff --cached --name-status | awk -e '$1 != "D" && $2 ~ /\.py$/ {print $2}')
if [[ ${#PYTHON_DIRS[@]} != 0 ]]; then
# Check if the project contains dirty Python files
$YAPF -r --diff "${PYTHON_DIRS[@]}"
exit_code=$?
echo YAPF exit code=$exit_code
if [[ "$exit_code" != 0 ]]; then
# If it does, fix them, show a warning, and quit (don't commit)
echo "YAPF detected lint errors."
echo "Fixing errors."
$YAPF -ir "${PYTHON_DIRS[@]}"
echo "Errors fixed. Please commit again."
exit $exit_code
else
echo "No lint error detected."
fi
else
echo "No .py files changed."
fi
exit 0
[pep8]
max-line-length = 88
ignore = E101,E114,E115,E116,E121,E128,E251,E265,E266,E3,E501,E711,E712,E713,E714,W191
[yapf]
# See https://github.com/google/yapf#id10
# and https://gist.github.com/krnd/3b8c5834c5c5c5097638ec10729787f7
based_on_style = pep8
align_closing_bracket_with_visual_indent = true
allow_multiline_lambdas = true
allow_multiline_dictionary_keys = false
allow_split_before_default_or_named_assigns = false
allow_split_before_dict_value = false
#arithmetic_precedence_indication = true
blank_line_before_nested_class_or_def = true
blank_line_before_module_docstring = false
blank_line_before_class_docstring = false
blank_lines_around_top_level_definition = 2
coalesce_brackets = false
column_limit = 120
continuation_align_style = VALIGN-RIGHT
continuation_indent_width = 4
dedent_closing_brackets = false
#disable_ending_comma_heuristic = true
each_dict_entry_on_separate_line = true
force_multiline_dict = false
#i18n_comment =
#i18n_function_call =
indent_dictionary_value = false
indent_width = 4
indent_blank_lines = false
indent_closing_brackets = false
join_multiple_lines = false
no_spaces_around_selected_binary_operators = true
spaces_around_power_operator = false
spaces_around_default_or_named_assign = false
spaces_around_dict_delimiters = false
spaces_around_list_delimiters = false
spaces_around_subscript_colon = false
spaces_around_tuple_delimiters = false
spaces_before_comment = 1
space_between_ending_comma_and_closing_bracket = false
space_inside_brackets = false
split_arguments_when_comma_terminated = false
split_all_comma_separated_values = false
split_all_top_level_comma_separated_values = false
split_before_bitwise_operator = false
split_before_arithmetic_operator = false
split_before_closing_bracket = true
split_before_dict_set_generator = false
split_before_dot = true
split_before_expression_after_opening_paren = true
split_before_first_argument = false
split_before_logical_operator = false
#split_before_named_assigns = true
split_complex_comprehension = false
split_penalty_after_opening_bracket = 30
split_penalty_after_unary_operator = 10000
split_penalty_arithmetic_operator = 10000
split_penalty_before_if_expr = 0
split_penalty_bitwise_operator = 300
split_penalty_comprehension = 80
#split_penalty_excess_character = 30
#split_penalty_for_added_line_split = 100
split_penalty_import_names = 10000
split_penalty_logical_operator = 300
use_tabs = true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment