Skip to content

Commit

Permalink
Add pre-commit hooks and format code
Browse files Browse the repository at this point in the history
  • Loading branch information
jkaeske committed Dec 28, 2023
1 parent aea3ee9 commit ec70b79
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 41 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install tox
pip install -r requirements.txt
- name: Run tests with tox
run: tox -e py${{ matrix.python-version }}
Expand Down
16 changes: 15 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,18 @@ repos:
rev: 23.12.1
hooks:
- id: black
language_version: python3.11
language_version: python3.11

- repo: https://github.com/PyCQA/flake8
rev: 6.1.0
hooks:
- id: flake8
args: ["--config=setup.cfg"]
additional_dependencies: [flake8-isort]

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
125 changes: 86 additions & 39 deletions django_i18n_lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

import os
import re
import sys
from optparse import OptionParser


Expand All @@ -16,7 +15,7 @@ def location(str, pos):
for char in str:
if counter == pos:
return lineno, charpos
elif char == '\n':
elif char == "\n":
lineno += 1
charpos = 1
counter += 1
Expand All @@ -26,11 +25,13 @@ def location(str, pos):

return lineno, charpos


# Things that are OK:
GOOD_STRINGS = re.compile(
r"""
(
# django comment
( {%\ comment\ %}.*?{%\ endcomment\ %}
{%\ comment\ %}.*?{%\ endcomment\ %}
# already translated text (until Django 3.0)
|{%\ ?blocktrans.*?{%\ ?endblocktrans\ ?%}
Expand Down Expand Up @@ -108,41 +109,40 @@ def location(str, pos):
# another common template comment
|{\#.*?\#}
)""",

# MULTILINE to match across lines and DOTALL to make . include the newline
re.MULTILINE | re.DOTALL | re.VERBOSE | re.IGNORECASE)
re.MULTILINE | re.DOTALL | re.VERBOSE | re.IGNORECASE,
)

# Stops us matching non-letter parts, e.g. just hypens, full stops etc.
LETTERS = re.compile(r"[^\W\d_]")

LEADING_TRAILING_WHITESPACE = re.compile(r"(^\W+|\W+$)")


def split_into_good_and_bad(template):
for index, match in enumerate(GOOD_STRINGS.split(template)):
yield (index, match)



def split_trailing_space(string):
"""Given a string, returns a tuple of 3 string, the leading whitespace, middle, and trailing whitespace"""
results = LEADING_TRAILING_WHITESPACE.split(string)
if len(results) == 1:
# no spaces
return ('', string, '')
elif len(results) == 3 and results[0] == '' and results[2] != '':
return ("", string, "")
elif len(results) == 3 and results[0] == "" and results[2] != "":
# only leading whitespace
return (results[1], results[2], '')
elif len(results) == 3 and results[0] != '' and results[2] == '':
return (results[1], results[2], "")
elif len(results) == 3 and results[0] != "" and results[2] == "":
# only trailing
return ('', results[0], results[1])
return ("", results[0], results[1])
elif len(results) == 5:
# leading and trailing whitespace
return (results[1], results[2], results[3])
else:
raise NotImplementedError("Unknown case: %r %r" % (string, results))



def replace_strings(filename, overwrite=False, force=False, accept=[]):
full_text_lines = []
with open(filename) as fp:
Expand All @@ -161,23 +161,29 @@ def replace_strings(filename, overwrite=False, force=False, accept=[]):
full_text_lines.append(string)
else:
# split out the leading whitespace and trailing
leading_whitespace, message, trailing_whitespace = split_trailing_space(string)
leading_whitespace, message, trailing_whitespace = split_trailing_space(
string
)
full_text_lines.append(leading_whitespace)

# Find location of first letter
lineno, charpos = location(string, offset+m.span()[0])
lineno, charpos = location(string, offset + m.span()[0])

if any(r.match(message) for r in accept):
full_text_lines.append(message)
elif lineno in ignore_lines:
full_text_lines.append(message)
elif force:
full_text_lines.append('{% translate "'+message.replace('"', '\\"')+'" %}')
full_text_lines.append(
'{% translate "' + message.replace('"', '\\"') + '" %}'
)

else:
change = raw_input("Make %r translatable? [Y/n] " % message)
if change == 'y' or change == "":
full_text_lines.append('{% translate "'+message.replace('"', '\\"')+'" %}')
change = input("Make %r translatable? [Y/n] " % message)
if change == "y" or change == "":
full_text_lines.append(
'{% translate "' + message.replace('"', '\\"') + '" %}'
)
else:
full_text_lines.append(message)

Expand All @@ -189,21 +195,20 @@ def replace_strings(filename, overwrite=False, force=False, accept=[]):
save_filename = filename
else:
save_filename = filename.split(".")[0] + "_translated.html"
open(save_filename, 'w').write(full_text)
open(save_filename, "w").write(full_text)
print("Fully translated! Saved as: %s" % save_filename)


def find_ignored_lines(template):
lines = set()
for m in re.finditer(r'{#\s*notrans\s*#}', template):
for m in re.finditer(r"{#\s*notrans\s*#}", template):
offset = m.span()[0]
lineno, charpos = location(template, offset)
lines.add(lineno)
return lines


def non_translated_text(template):

offset = 0

ignore_lines = find_ignored_lines(template)
Expand All @@ -212,16 +217,19 @@ def non_translated_text(template):
# taken from http://www.technomancy.org/python/strings-that-dont-match-regex/
for index, match in split_into_good_and_bad(template):
if index % 2 == 0:

# Ignore it if it doesn't have letters
m = LETTERS.search(match)
if m:
# Get location of first letter
lineno, charpos = location(template, offset+m.span()[0])
lineno, charpos = location(template, offset + m.span()[0])
if lineno in ignore_lines:
offset += len(match)
continue
yield (lineno, charpos, match.strip().replace("\n", "").replace("\r", "")[:120])
yield (
lineno,
charpos,
match.strip().replace("\n", "").replace("\r", "")[:120],
)

offset += len(match)

Expand All @@ -241,24 +249,60 @@ def filenames_to_work_on(directory, exclude_filenames):
"""Return list of files in directory that we should look at"""
files = []
for dirpath, dirs, filenames in os.walk(directory):
files.extend(os.path.join(dirpath, fname)
for fname in filenames
if (fname.endswith('.html') or fname.endswith('.txt')) and fname not in exclude_filenames)
files.extend(
os.path.join(dirpath, fname)
for fname in filenames
if (fname.endswith(".html") or fname.endswith(".txt"))
and fname not in exclude_filenames
)
return files


def main():
parser = OptionParser(usage="usage: %prog [options] <filenames>")
parser.add_option("-r", "--replace", action="store_true", dest="replace",
help="Ask to replace the strings in the file.", default=False)
parser.add_option("-o", "--overwrite", action="store_true", dest="overwrite",
help="When replacing the strings, overwrite the original file. If not specified, the file will be saved in a seperate file named X_translated.html", default=False)
parser.add_option("-f", "--force", action="store_true", dest="force",
help="Force to replace string with no questions", default=False)
parser.add_option("-e", "--exclude", action="append", dest="exclude_filename",
help="Exclude these filenames from being linted", default=[])
parser.add_option("-x", "--accept", action="append", dest="accept",
help="Exclude these regexes from results", default=[])
parser.add_option(
"-r",
"--replace",
action="store_true",
dest="replace",
help="Ask to replace the strings in the file.",
default=False,
)
parser.add_option(
"-o",
"--overwrite",
action="store_true",
dest="overwrite",
help=(
"When replacing the strings, overwrite the original file. If not specified, "
"the file will be saved in a separate file named X_translated.html"
),
default=False,
)
parser.add_option(
"-f",
"--force",
action="store_true",
dest="force",
help="Force to replace string with no questions",
default=False,
)
parser.add_option(
"-e",
"--exclude",
action="append",
dest="exclude_filename",
help="Exclude these filenames from being linted",
default=[],
)
parser.add_option(
"-x",
"--accept",
action="append",
dest="accept",
help="Exclude these regexes from results",
default=[],
)
(options, args) = parser.parse_args()

# Create a list of files to check
Expand All @@ -275,9 +319,12 @@ def main():

for filename in files:
if options.replace:
replace_strings(filename, overwrite=True, force=options.force, accept=accept_regexes)
replace_strings(
filename, overwrite=True, force=options.force, accept=accept_regexes
)
else:
print_strings(filename, accept=accept_regexes)

if __name__ == '__main__':

if __name__ == "__main__":
main()
3 changes: 3 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[flake8]
max-line-length = 120
exclude = .tox,.git

0 comments on commit ec70b79

Please sign in to comment.