Skip to content

Commit

Permalink
chore: Add ruff lint configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
jpmckinney committed Sep 16, 2024
1 parent 30f9ee6 commit 304f099
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 31 deletions.
10 changes: 3 additions & 7 deletions manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ def cli():
@click.argument('path')
@click.option('--match', help='')
def download_extensions(path, match):
"""
Download all registered extensions to a directory.
"""
"""Download all registered extensions to a directory."""
path = path.rstrip('/')

registry = ExtensionRegistry(extension_versions_url)
Expand All @@ -36,7 +34,7 @@ def download_extensions(path, match):
if not os.path.isdir(directory):
command = ['git', 'clone', version.repository_url, directory]
click.echo(' '.join(command))
subprocess.call(command)
subprocess.call(command) # noqa: S603 # trusted input


@cli.command()
Expand Down Expand Up @@ -96,9 +94,7 @@ def set_topics():

@cli.command()
def check_aspell_dictionary():
"""
Check whether ~/.aspell.en.pws contains unwanted words.
"""
"""Check whether ~/.aspell.en.pws contains unwanted words."""
with open(os.path.expanduser('~/.aspell.en.pws'), encoding='iso-8859-1') as f:
aspell = f.read()

Expand Down
25 changes: 25 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
[project]
name = "standard-maintenance-scripts"
version = "0.0.0"

[tool.ruff]
line-length = 119
target-version = "py310"

[tool.ruff.lint]
select = ["ALL"]
ignore = [
"ANN", "C901", "COM812", "D203", "D212", "D415", "EM", "ISC001", "PERF203", "PLR091", "Q000",
"D1",
"PTH",
"B028", # warnings
"FIX002", # todo
]

[tool.ruff.lint.flake8-unused-arguments]
ignore-variadic-names = true

[tool.ruff.lint.pep8-naming]
extend-ignore-names = ["visit_*"]

[tool.ruff.lint.per-file-ignores]
"tests/*" = [
"ARG001", "D", "FBT003", "INP001", "PLR2004", "S", "TRY003",
]
33 changes: 19 additions & 14 deletions tests/script.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ IGNORE=(
D415 # ends-in-punctuation (D400 ends-in-period)
# https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules
COM812 # missing-trailing-comma (ruff format)
ISC001 # single-line-implicit-string-concatenation (ruff format)
Q000 # bad-quotes-inline-string (ruff format)

# Complexity
Expand All @@ -66,6 +67,7 @@ IGNORE=(
# Specific repositories
B028 # no-explicit-stacklevel [jscc, ocds-merge, sample-data, standard, standard-maintenance-scripts]
EXE003 # shebang-missing-python [deploy]
SLF001 # private-member-access [cove-ocds, kingfisher-collect, pelican-frontend]
)
if [ -n "$REQUIREMENTS_FILE" ]; then
if grep babel $REQUIREMENTS_FILE > /dev/null; then
Expand All @@ -86,20 +88,24 @@ if [ -n "$REQUIREMENTS_FILE" ]; then
PT # pytest
DJ008 # django-model-without-dunder-str
S308 # suspicious-mark-safe-usage (false positive)
# https://docs.djangoproject.com/en/4.2/topics/http/views/
# signals.py https://docs.djangoproject.com/en/4.2/topics/signals/
# views.py https://docs.djangoproject.com/en/4.2/topics/http/views/
# migrations/ https://docs.djangoproject.com/en/4.2/howto/writing-migrations/
ARG001 # unused-function-argument
# https://docs.djangoproject.com/en/4.2/topics/class-based-views/
# admin.py https://docs.djangoproject.com/en/4.2/ref/contrib/admin/#modeladmin-methods
# routers.py https://docs.djangoproject.com/en/4.2/topics/db/multi-db/#an-example
# views.py https://docs.djangoproject.com/en/4.2/topics/class-based-views/
# commands.py https://docs.djangoproject.com/en/4.2/howto/custom-management-commands/
ARG002 # unused-method-argument
# https://docs.djangoproject.com/en/4.2/ref/models/options/#constraints
# https://docs.djangoproject.com/en/4.2/ref/models/options/#indexes
# https://docs.djangoproject.com/en/4.2/topics/forms/modelforms/
# https://docs.djangoproject.com/en/4.2/topics/migrations/#migration-files
# https://docs.djangoproject.com/en/4.2/topics/db/multi-db/#an-example
# https://docs.djangoproject.com/en/4.2/topics/db/fixtures/#how-to-use-a-fixture
# https://www.django-rest-framework.org/api-guide/serializers/#modelserializer
# admin.py https://docs.djangoproject.com/en/4.2/ref/contrib/admin/
# forms.py https://docs.djangoproject.com/en/4.2/topics/forms/modelforms/
# models.py https://docs.djangoproject.com/en/4.2/ref/models/options/
# serializers.py https://www.django-rest-framework.org/api-guide/serializers/#modelserializer
# translation.py https://django-modeltranslation.readthedocs.io/en/latest/registration.html#required-langs
# views.py https://www.django-rest-framework.org/api-guide/viewsets/
# migrations/ https://docs.djangoproject.com/en/4.2/topics/migrations/#migration-files
# tests/ https://docs.djangoproject.com/en/4.2/topics/db/fixtures/#how-to-use-a-fixture
RUF012 # mutable-class-default
# https://docs.djangoproject.com/en/4.2/ref/models/meta/
SLF001 # private-member-access
)
BUILTINS_IGNORELIST+=(
"'id'" # path component
Expand Down Expand Up @@ -232,13 +238,12 @@ ruff check . --select ALL \
IFS=,
echo "${PER_FILE_IGNORES[*]}"
)" \
--config 'line-length = 119' \
--config "lint.allowed-confusables = ['’']" \
--config "lint.flake8-builtins.builtins-ignorelist = [$(
IFS=,
echo "${BUILTINS_IGNORELIST[*]}"
)]" \
--config 'lint.flake8-self.extend-ignore-names = ["_job"]' \
--config "lint.allowed-confusables = ['’']" \
--config 'line-length = 119' \
--exclude 'demo_docs,t'

if [ -n "$CI" ]; then
Expand Down
20 changes: 10 additions & 10 deletions tests/test_requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def visit_keyword(self, node):
for elt in node.value.elts:
self.mapping.update(projects_and_modules(val(elt)))
elif node.arg == 'extras_require' and self.extras:
for key, value in zip(node.value.keys, node.value.values):
for key, value in zip(node.value.keys, node.value.values, strict=True):
if val(key) in self.extras:
for elt in value.elts:
self.mapping.update(projects_and_modules(val(elt)))
Expand All @@ -123,32 +123,32 @@ def __init__(self, filename, packages):
if self.path.name == 'setup.py':
self.excluded.add('setuptools')

def visit_Try(self, node): # noqa: N802 # false positive
def visit_Try(self, node):
# Don't collect imports in `try: ... except ImportError: ...` blocks.
if not any(h.type.id == 'ImportError' for h in node.handlers if isinstance(h.type, ast.Name)):
self.generic_visit(node)

def visit_If(self, node): # noqa: N802 # false positive
def visit_If(self, node):
# Don't collect imports in `if sys.version_info >= (3, 8): ... else: ...` blocks.
if (
not isinstance(node.test, ast.Compare)
or any(isinstance(op, (ast.In, ast.NotIn, ast.Is, ast.IsNot)) for op in node.test.ops)
or any(isinstance(op, ast.In | ast.NotIn | ast.Is | ast.IsNot) for op in node.test.ops)
or isinstance(node.test.left, ast.Tuple)
or not any(
isinstance(val(e), int) for c in node.test.comparators if isinstance(c, ast.Tuple) for e in c.elts
)
):
self.generic_visit(node)

def visit_Import(self, node): # noqa: N802 # false positive
def visit_Import(self, node):
for alias in node.names:
self.add(alias.name)

def visit_ImportFrom(self, node): # noqa: N802 # false positive
def visit_ImportFrom(self, node):
if node.module and not node.level:
self.add(node.module)

def visit_Assign(self, node): # noqa: N802 # false positive
def visit_Assign(self, node):
# Handle Django settings.py file.
if self.path.name == 'settings.py' or self.path.parent.name == 'settings':
for target in node.targets:
Expand All @@ -166,15 +166,15 @@ def visit_Assign(self, node): # noqa: N802 # false positive
# A requirement might be required by a backend.
elif target.id == 'CACHES':
for value in node.value.values: # noqa: PD011 # false positive
for k, v in zip(value.keys, value.values):
for k, v in zip(value.keys, value.values, strict=True):
if val(k) == 'BACKEND':
if val(v) == 'django.core.cache.backends.memcached.MemcachedCache':
self.add('memcache')
elif val(v) == 'django.core.cache.backends.memcached.PyMemcacheCache':
self.add('pymemcache')
elif target.id == 'CHANNEL_LAYERS':
for value in node.value.values: # noqa: PD011 # false positive
for k, v in zip(value.keys, value.values):
for k, v in zip(value.keys, value.values, strict=True):
if val(k) in 'BACKEND' and val(v) == 'channels_redis.core.RedisChannelLayer':
self.add('channels_redis')
elif target.id == 'DATABASES':
Expand All @@ -192,7 +192,7 @@ def visit_Assign(self, node): # noqa: N802 # false positive
if default and urlsplit(val(default.value)).scheme == 'postgresql':
self.add('psycopg2')
elif isinstance(value, ast.Dict):
for k, v in zip(value.keys, value.values):
for k, v in zip(value.keys, value.values, strict=True):
if val(k) == 'ENGINE' and val(v) in (
'django.db.backends.postgresql',
'django.db.backends.postgresql_psycopg2',
Expand Down

0 comments on commit 304f099

Please sign in to comment.