Skip to content

Commit ba942f2

Browse files
authored
Replace Python toolchain with Ruff (#3546)
1 parent 34f8ec8 commit ba942f2

File tree

17 files changed

+70
-101
lines changed

17 files changed

+70
-101
lines changed

.pre-commit-config.yaml

Lines changed: 4 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -35,53 +35,13 @@ repos:
3535
- id: check-docstring-first
3636
- id: requirements-txt-fixer
3737

38-
# Use the `.isort.cfg` file to configure additional project-specific requirements.
39-
- repo: https://github.com/PyCQA/isort
40-
rev: 5.12.0
38+
- repo: https://github.com/astral-sh/ruff-pre-commit
39+
rev: v0.1.8
4140
hooks:
42-
- id: isort
43-
files: \.py$
44-
exclude: ^build/.*$|^.tox/.*$|^venv/.*$
45-
args:
46-
- --profile=black
47-
- --lines-after-imports=2
48-
49-
- repo: https://github.com/asottile/pyupgrade
50-
rev: v3.15.0
51-
hooks:
52-
- id: pyupgrade
53-
args:
54-
- --py310-plus
55-
56-
- repo: https://github.com/charliermarsh/ruff-pre-commit
57-
rev: "v0.1.7"
58-
hooks:
59-
- id: ruff
41+
- id: ruff # replaces Flake8, isort, pydocstyle, pyupgrade
6042
args:
6143
- --fix
62-
63-
- repo: https://github.com/ambv/black
64-
rev: 23.11.0
65-
hooks:
66-
- id: black
67-
args:
68-
- --safe
69-
70-
- repo: https://github.com/pycqa/pydocstyle
71-
rev: 6.3.0
72-
hooks:
73-
- id: pydocstyle
74-
args:
75-
- --convention=pep257
76-
# Additional ignore reasons:
77-
# D1xx: we do not want to force contributors to write redundant or useless docstrings
78-
# D202: additional whitespace helps with readability
79-
# D205: we don't want to always require a single line summary
80-
# D211: same as D202
81-
# D400: first line doesn't need to end in a period
82-
# See the following documentation for what each rule does:
83-
# https://www.pydocstyle.org/en/6.2.3/error_codes.html#error-codes
84-
- --add-ignore=D1,D202,D205,D211,D400
44+
- id: ruff-format # replaces Black
8545

8646
# Use the `.prettierignore` and `.prettier.config.js` files to configure project-specific requirements.
8747
- repo: https://github.com/pre-commit/mirrors-prettier

api/api/views/image_views.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ async def thumbnail(self, *args, **kwargs):
153153
@action(detail=True, url_path="watermark", url_name="watermark")
154154
def watermark(self, request, *_, **__): # noqa: D401
155155
"""
156-
This endpoint is deprecated.
156+
Note that this endpoint is deprecated.
157157
158158
---
159159

api/test/unit/controllers/test_search_controller.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -618,9 +618,7 @@ def test_no_post_process_results_recursion(
618618
# Ensure dead link filtering does not remove any results
619619
pook.head(
620620
pook.regex(rf"{MOCK_LIVE_RESULT_URL_PREFIX}/\d"),
621-
).times(
622-
hit_count
623-
).reply(200)
621+
).times(hit_count).reply(200)
624622

625623
serializer = image_media_type_config.search_request_serializer(
626624
# This query string does not matter, ultimately, as pook is mocking

api/test/unit/utils/test_image_proxy.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,7 @@ def auth_key():
5959

6060
@pytest.fixture
6161
def photon_get(session_loop):
62-
"""
63-
Run ``image_proxy.get`` and wait for all tasks to finish.
64-
"""
62+
"""Run ``image_proxy.get`` and wait for all tasks to finish."""
6563

6664
def do(*args, **kwargs):
6765
try:

api/test/unit/utils/test_watermark.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
@dataclass
1818
class RequestsFixture:
1919
requests: list[Request]
20-
response_factory: Callable[ # noqa: E731
21-
[Request], Response
22-
] = lambda x: RequestsFixture._default_response_factory(x)
20+
response_factory: Callable[[Request], Response] = ( # noqa: E731
21+
lambda x: RequestsFixture._default_response_factory(x)
22+
)
2323

2424
@staticmethod
2525
def _default_response_factory(req: Request) -> Response:

api/test/unit/utils/test_waveform.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919
@dataclass
2020
class RequestsFixture:
2121
requests: list[Request]
22-
response_factory: Callable[ # noqa: E731
23-
[Request], Response
24-
] = lambda x: RequestsFixture._default_response_factory(x)
22+
response_factory: Callable[[Request], Response] = ( # noqa: E731
23+
lambda x: RequestsFixture._default_response_factory(x)
24+
)
2525

2626
@staticmethod
2727
def _default_response_factory(req: Request) -> Response:

api/test/unit/views/test_media_views.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,7 @@ def test_list_query_count(api_client, media_type_config):
3232
), patch(
3333
"api.serializers.media_serializers.search_controller",
3434
get_sources=MagicMock(return_value={}),
35-
), pytest_django.asserts.assertNumQueries(
36-
1
37-
):
35+
), pytest_django.asserts.assertNumQueries(1):
3836
res = api_client.get(f"/v1/{media_type_config.url_prefix}/")
3937

4038
assert res.status_code == 200

catalog/dags/common/loader/reporting.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,7 @@ def skip_report_completion(
9393
) -> bool:
9494
return (
9595
# Duration must be provided and be a value greater than 1 second
96-
duration is None
97-
or duration in ("inf", "less than 1 sec")
96+
duration is None or duration in ("inf", "less than 1 sec")
9897
) and (
9998
# Record counts by media type must be provided and at least one value must
10099
# be truthy (i.e. not None)

catalog/tests/dags/common/loader/test_sql.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ def postgres_with_load_and_image_table(load_table, sql_info, mock_pg_hook_task):
8585

8686
def _load_local_tsv(tmpdir, bucket, tsv_file_name, identifier, mock_pg_hook_task):
8787
"""
88-
This wraps sql.load_local_data_to_intermediate_table so we can test it
89-
under various conditions.
88+
Wrap ``sql.load_local_data_to_intermediate_table`` so we can test it under
89+
various conditions.
9090
"""
9191
tsv_file_path = os.path.join(RESOURCES, tsv_file_name)
9292
with open(tsv_file_path) as f:

catalog/tests/dags/common/test_log_cleanup.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,10 @@ def calculate_cutoffs():
4646

4747

4848
def test_log_cleaner_leaves_new_files():
49-
"""If all the log files are newer than the maxLogAgeInDays,
50-
no log files are deleted"""
49+
"""
50+
If all the log files are newer than the maxLogAgeInDays,
51+
no log files are deleted
52+
"""
5153
log_files_count = len(list(Path.glob(logs_folder, "**/*.log")))
5254
assert log_files_count == INITIAL_LOG_FILE_COUNT
5355

@@ -61,8 +63,10 @@ def test_log_cleaner_leaves_new_files():
6163

6264

6365
def test_log_cleaner_deletes_only_old_files():
64-
"""Log cleaner deletes all the log files that are older than
65-
maxLogAgeInDays, but leaves the files that are newer"""
66+
"""
67+
Log cleaner deletes all the log files that are older than
68+
maxLogAgeInDays, but leaves the files that are newer
69+
"""
6670
deleted_folders = log_cleanup.clean_up(
6771
logs_folder, cutoffs_in_days[1], ENABLE_DELETE
6872
)

catalog/tests/dags/providers/provider_api_scripts/test_cc_mixter.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
"""
2-
Run these tests locally with `just test -k cc_mixter`
3-
"""
1+
"""Run these tests locally with `just test -k cc_mixter`"""
42

53
import json
64
from pathlib import Path

catalog/tests/dags/providers/provider_api_scripts/test_provider_data_ingester.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ def test_ingest_records_uses_query_params_list_from_dagrun_conf():
395395

396396

397397
def test_ingest_records_raises_IngestionError():
398-
with (patch.object(ingester, "get_batch") as get_batch_mock,):
398+
with patch.object(ingester, "get_batch") as get_batch_mock:
399399
get_batch_mock.side_effect = [
400400
Exception("Mock exception message"),
401401
(EXPECTED_BATCH_DATA, True), # Second batch should not be reached

catalog/tests/dags/providers/provider_api_scripts/test_time_delineated_provider_data_ingester.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,15 @@ def test_get_timestamp_pairs_returns_full_day_when_few_records_found():
9191
def test_get_timestamp_pairs_with_large_record_counts():
9292
with patch.object(ingester, "_get_record_count") as mock_count:
9393
# Mock the calls to _get_record_count in order
94-
mock_count.side_effect = [
95-
150_000, # Getting total count for the entire day
96-
0, # Get count for first hour, count == 0
97-
10, # Get count for second hour, count < max_records
98-
101_000, # Get count for third hour, count > division_threshold
99-
49_090, # Get count for fourth hour, max_records < count < division_threshold
100-
] + list(
101-
repeat(0, 20)
94+
mock_count.side_effect = (
95+
[
96+
150_000, # Getting total count for the entire day
97+
0, # Get count for first hour, count == 0
98+
10, # Get count for second hour, count < max_records
99+
101_000, # Get count for third hour, count > division_threshold
100+
49_090, # Get count for fourth hour, max_records < count < division_threshold
101+
]
102+
+ list(repeat(0, 20))
102103
) # Fill list with count == 0 for the remaining hours
103104

104105
# We only get timestamp pairs for the hours that had records. For the

catalog/tests/dags/providers/test_factory_utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ def dagrun_mock() -> DagRun:
2323
@pytest.fixture
2424
def internal_func_mock():
2525
"""
26+
Get an empty ``MagicMock`` instance.
27+
2628
This mock, along with the value, get handed into the provided function.
2729
For fake_provider_module.main, the mock will be called with the provided value.
2830
"""

catalog/tests/utilities/dag_doc_gen/test_dag_doc_generation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ def test_generate_dag_doc():
185185
this one has a doc
186186
"""
187187
)
188-
with (mock.patch(f"{_MODULE}.get_dags_info") as get_dags_info_mock,):
188+
with mock.patch(f"{_MODULE}.get_dags_info") as get_dags_info_mock:
189189
# Return in reverse order to ensure they show up in the correct order
190190
get_dags_info_mock.return_value = [
191191
DagInfo("b", None, "this one has a doc", "t1", False, None),

documentation/_ext/link_issues.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ def apply(self) -> None:
122122
if len(match.groups()) != 1:
123123
raise ValueError(
124124
"issuetracker_issue_pattern must have "
125-
"exactly one group: {!r}".format(match.groups())
125+
f"exactly one group: {match.groups()!r}"
126126
)
127127

128128
# extract the text between the last issue reference and the

pyproject.toml

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,39 @@
66
exclude-file = ".codespell/ignore_lines.txt"
77
ignore-words = ".codespell/ignore_words.txt"
88

9-
[tool.ruff.per-file-ignores]
10-
"*test*" = ["E501"]
11-
"*__init__*" = ["F401"]
12-
"*settings/__init__.py" = ["F403"]
9+
[tool.ruff.lint]
10+
# Default for `select` is ["E4", "E7", "E9", "F"]
11+
# All additional rules must be added using `extend-select`.
12+
extend-select = [
13+
"UP",
14+
"D", # pydocstyle, https://docs.astral.sh/ruff/rules/#pydocstyle-d
15+
]
16+
ignore = [
17+
"D1", # D1xx: we do not want to force contributors to write redundant or useless docstrings
18+
"D202", # D202: additional whitespace helps with readability
19+
"D205", # D205: we don't want to always require a single line summary
20+
"D211", # D211: same as D202
21+
"D400", # D400: first line doesn't need to end in a period
22+
]
1323

14-
[tool.isort]
15-
profile = "black"
24+
[tool.ruff.lint.per-file-ignores]
25+
"*test*" = ["E501"] # Tests should have long, descriptive names.
26+
"*__init__*" = ["F401"] # Init files usually have unused imports.
1627

17-
sections = [
18-
"FUTURE",
19-
"STDLIB",
20-
"DJANGO",
21-
"THIRDPARTY",
22-
"FIRSTPARTY",
23-
"LOCALFOLDER",
24-
]
28+
[tool.ruff.lint.pydocstyle]
29+
convention = "pep257"
2530

26-
known_django = [
31+
[tool.ruff.lint.isort]
32+
lines-after-imports = 2
33+
section-order = [
34+
"future",
35+
"standard-library",
2736
"django",
28-
"rest_framework",
37+
"third-party",
38+
"first-party",
39+
"local-folder",
2940
]
30-
31-
known_first_party = [
41+
known-first-party = [
3242
"conf",
3343
"api",
3444
"ingestion_server",
@@ -41,4 +51,5 @@ known_first_party = [
4151
"retired",
4252
]
4353

44-
lines_after_imports = 2
54+
[tool.ruff.lint.isort.sections]
55+
django = ["django", "rest_framework"]

0 commit comments

Comments
 (0)