Skip to content

Commit 769e76f

Browse files
authored
Replace all linter and validation libraries by ruff (#1333)
Signed-off-by: tdruez <tdruez@nexb.com>
1 parent de5a504 commit 769e76f

22 files changed

+100
-106
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,10 @@ jobs:
4242
with:
4343
python-version: ${{ matrix.python-version }}
4444

45-
- name: Install universal ctags
46-
run: sudo apt-get install -y universal-ctags
47-
48-
- name: Install xgettext
49-
run: sudo apt-get install -y gettext
45+
- name: Install universal ctags and xgettext
46+
run: |
47+
sudo apt-get update
48+
sudo apt-get install -y universal-ctags gettext
5049
5150
- name: Install dependencies
5251
run: make dev envfile

Makefile

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ PYTHON_EXE?=python3
2525
MANAGE=bin/python manage.py
2626
ACTIVATE?=. bin/activate;
2727
VIRTUALENV_PYZ=etc/thirdparty/virtualenv.pyz
28-
BLACK_ARGS=--exclude=".cache|migrations|data|lib|bin|var"
2928
# Do not depend on Python to generate the SECRET_KEY
3029
GET_SECRET_KEY=`head -c50 /dev/urandom | base64 | head -c50`
3130
# Customize with `$ make envfile ENV_FILE=/etc/scancodeio/.env`
@@ -63,33 +62,22 @@ envfile:
6362
@mkdir -p $(shell dirname ${ENV_FILE}) && touch ${ENV_FILE}
6463
@echo SECRET_KEY=\"${GET_SECRET_KEY}\" > ${ENV_FILE}
6564

66-
isort:
67-
@echo "-> Apply isort changes to ensure proper imports ordering"
68-
@${ACTIVATE} isort --profile black .
69-
70-
black:
71-
@echo "-> Apply black code formatter"
72-
@${ACTIVATE} black ${BLACK_ARGS} .
73-
7465
doc8:
7566
@echo "-> Run doc8 validation"
7667
@${ACTIVATE} doc8 --max-line-length 100 --ignore-path docs/_build/ --quiet docs/
7768

78-
valid: isort black doc8 check
79-
80-
bandit:
81-
@echo "-> Run source code security analyzer"
82-
@${ACTIVATE} bandit -r scanpipe scancodeio --quiet --exclude test_spdx.py
69+
valid:
70+
@echo "-> Run Ruff linter"
71+
@${ACTIVATE} ruff check --fix
72+
@echo "-> Run Ruff format"
73+
@${ACTIVATE} ruff format
8374

84-
check: doc8 bandit
85-
@echo "-> Run flake8 (pycodestyle, pyflakes, mccabe) validation"
86-
@${ACTIVATE} flake8 .
87-
@echo "-> Run isort imports ordering validation"
88-
@${ACTIVATE} isort --profile black --check-only .
89-
@echo "-> Run black validation"
90-
@${ACTIVATE} black --check ${BLACK_ARGS} .
91-
@echo "-> Run docstring validation"
92-
@${ACTIVATE} pydocstyle scanpipe scancodeio
75+
check:
76+
@echo "-> Run Ruff linter validation (pycodestyle, bandit, isort, and more)"
77+
@${ACTIVATE} ruff check
78+
@echo "-> Run Ruff format validation"
79+
@${ACTIVATE} ruff format --check
80+
@$(MAKE) doc8
9381

9482
check-deploy:
9583
@echo "-> Check Django deployment settings"
@@ -163,4 +151,4 @@ offline-package: docker-images
163151
@mkdir -p dist/
164152
@tar -cf dist/scancodeio-offline-package-`git describe --tags`.tar build/
165153

166-
.PHONY: virtualenv conf dev envfile install check bandit valid isort check-deploy clean migrate upgrade postgresdb sqlitedb backupdb run test docs bump docker-images offline-package
154+
.PHONY: virtualenv conf dev envfile install doc8 check valid check-deploy clean migrate upgrade postgresdb sqlitedb backupdb run test docs bump docker-images offline-package

pyproject.toml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
[tool.ruff]
2+
line-length = 88
3+
exclude = [
4+
"migrations",
5+
"bin",
6+
"Script",
7+
"Lib",
8+
"lib",
9+
"lib64",
10+
"local",
11+
"var",
12+
]
13+
14+
[tool.ruff.lint]
15+
# Rules: https://docs.astral.sh/ruff/rules/
16+
select = [
17+
"E", # pycodestyle
18+
"W", # pycodestyle warnings
19+
"D", # pydocstyle
20+
"F", # Pyflakes
21+
"UP", # pyupgrade
22+
"S", # flake8-bandit
23+
"I", # isort
24+
"C9", # McCabe complexity
25+
]
26+
ignore = ["D1", "D203", "D205", "D212", "D400", "D415"]
27+
28+
[tool.ruff.lint.isort]
29+
force-single-line = true
30+
sections = { django = ["django"] }
31+
section-order = [
32+
"future",
33+
"standard-library",
34+
"django",
35+
"third-party",
36+
"first-party",
37+
"local-folder",
38+
]
39+
40+
[tool.ruff.lint.mccabe]
41+
max-complexity = 10
42+
43+
[tool.ruff.lint.per-file-ignores]
44+
# All usage of assert on the SPDX test file.
45+
"**/test_spdx.py*" = ["S101"]

scancodeio/settings.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,9 @@
245245

246246
AUTH_PASSWORD_VALIDATORS = [
247247
{
248-
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
248+
"NAME": (
249+
"django.contrib.auth.password_validation.UserAttributeSimilarityValidator"
250+
),
249251
},
250252
{
251253
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",

scanpipe/filters.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ def filter_queryset(self, queryset):
260260
@classmethod
261261
def filter_for_lookup(cls, field, lookup_type):
262262
"""Add support for JSONField storing "list" using the JSONListFilter."""
263-
if isinstance(field, models.JSONField) and field.default == list:
263+
if isinstance(field, models.JSONField) and field.default is list:
264264
return JSONContainsFilter, {}
265265

266266
return super().filter_for_lookup(field, lookup_type)

scanpipe/models.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2713,8 +2713,7 @@ def walk(self, topdown=True):
27132713
for child in self.children().iterator():
27142714
if topdown:
27152715
yield child
2716-
for subchild in child.walk(topdown=topdown):
2717-
yield subchild
2716+
yield from child.walk(topdown=topdown)
27182717
if not topdown:
27192718
yield child
27202719

@@ -2739,7 +2738,7 @@ def file_content(self):
27392738
# This workaround ensures that the entire content of map files is displayed.
27402739
file_type = get_type(self.location)
27412740
if file_type.is_js_map:
2742-
with open(self.location, "r") as file:
2741+
with open(self.location) as file:
27432742
content = json.load(file)
27442743

27452744
return json.dumps(content, indent=2)

scanpipe/pipes/codebase.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,7 @@ def walk(self, topdown=True):
138138
for root_resource in self.root_resources:
139139
if topdown:
140140
yield root_resource
141-
for resource in root_resource.walk(topdown=topdown):
142-
yield resource
141+
yield from root_resource.walk(topdown=topdown)
143142
if not topdown:
144143
yield root_resource
145144

scanpipe/pipes/fetch.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
import logging
2626
import os
2727
import re
28-
import subprocess # nosec
28+
import subprocess
2929
import tempfile
3030
from collections import namedtuple
3131
from pathlib import Path
@@ -68,7 +68,7 @@ def run_command_safely(command_args):
6868
6969
Raise a SubprocessError if the exit code was non-zero.
7070
"""
71-
completed_process = subprocess.run( # nosec
71+
completed_process = subprocess.run( # noqa: S603
7272
command_args,
7373
capture_output=True,
7474
text=True,

scanpipe/pipes/input.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,9 +163,9 @@ def clean_xlsx_field_value(model_class, field_name, value):
163163
return [{dict_key: entry} for entry in value.splitlines()]
164164

165165
elif isinstance(field, models.JSONField):
166-
if field.default == list:
166+
if field.default is list:
167167
return value.splitlines()
168-
elif field.default == dict:
168+
elif field.default is dict:
169169
return # dict stored as JSON are not supported
170170

171171
return value

scanpipe/pipes/js.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ def sha1(content):
7878
# The following hash is not used in any security context. It is only used
7979
# to generate a value for matching purposes, collisions are acceptable and
8080
# "content" is not coming from user-generated input.
81-
return hashlib.sha1(content.encode()).hexdigest() # nosec
81+
return hashlib.sha1(content.encode(), usedforsecurity=False).hexdigest()
8282

8383

8484
def source_content_sha1_list(map_file):

0 commit comments

Comments
 (0)