Skip to content

Commit b9da4c9

Browse files
authored
Migrate to ruff (#96)
1 parent 7a27c68 commit b9da4c9

18 files changed

+228
-151
lines changed

.bandit.yml

Lines changed: 0 additions & 3 deletions
This file was deleted.

.bumpversion.cfg

Lines changed: 0 additions & 6 deletions
This file was deleted.

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
fail-fast: false
1919
matrix:
2020
include:
21-
- python-version: '3.12' # Keep in sync with .readthedocs.yml
21+
- python-version: '3.13' # Keep in sync with .readthedocs.yml
2222
env:
2323
TOXENV: docs
2424
- python-version: '3.13'

.pre-commit-config.yaml

Lines changed: 6 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,7 @@
1-
default_language_version:
2-
python: python3.13
31
repos:
4-
- hooks:
5-
- id: bandit
6-
args: [-r, -c, .bandit.yml]
7-
repo: https://github.com/PyCQA/bandit
8-
rev: 1.8.2
9-
- hooks:
10-
- id: black
11-
language_version: python3
12-
repo: https://github.com/ambv/black
13-
rev: 24.10.0
14-
- hooks:
15-
- id: isort
16-
language_version: python3
17-
repo: https://github.com/PyCQA/isort
18-
rev: 5.13.2
19-
- hooks:
20-
- id: flake8
21-
language_version: python3
22-
additional_dependencies:
23-
- flake8-bugbear
24-
- flake8-comprehensions
25-
- flake8-debugger
26-
- flake8-docstrings
27-
- flake8-string-format
28-
repo: https://github.com/pycqa/flake8
29-
rev: 7.1.1
30-
- hooks:
31-
- id: pyupgrade
32-
args: [--py39-plus]
33-
repo: https://github.com/asottile/pyupgrade
34-
rev: v3.19.1
2+
- repo: https://github.com/astral-sh/ruff-pre-commit
3+
rev: v0.9.7
4+
hooks:
5+
- id: ruff
6+
args: [ --fix ]
7+
- id: ruff-format

.readthedocs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ sphinx:
66
build:
77
os: ubuntu-22.04
88
tools:
9-
python: "3.12" # Keep in sync with .github/workflows/main.yml
9+
python: "3.13" # Keep in sync with .github/workflows/main.yml
1010

1111
python:
1212
install:

docs/_ext/github.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Optional
1+
from __future__ import annotations
22

33
from docutils import nodes
44
from docutils.parsers.rst.roles import set_classes
@@ -14,11 +14,10 @@ def github_role(
1414
text,
1515
lineno,
1616
inliner,
17-
options: Optional[dict] = None,
18-
content: Optional[list] = None,
17+
options: dict[str, str] | None = None,
18+
content=None,
1919
):
2020
options = options or {}
21-
content = content or []
2221
if text.isdigit():
2322
display_text = f"#{text}"
2423
url = f"https://github.com/scrapy/itemloaders/issues/{text}"

docs/conf.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@
1111
# serve to show the default.
1212

1313
import sys
14-
from os import path
14+
from pathlib import Path
1515

1616
# If your extensions are in another directory, add it here. If the directory
1717
# is relative to the documentation root, use os.path.abspath to make it
1818
# absolute, like shown here.
19-
sys.path.append(path.dirname(__file__))
20-
sys.path.insert(0, path.dirname(path.dirname(__file__)))
19+
sys.path.append(str(Path(__file__).parent))
20+
sys.path.insert(0, str(Path(__file__).parent.parent))
2121

2222
# General configuration
2323
# ---------------------

itemloaders/__init__.py

Lines changed: 19 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,21 @@
66

77
from __future__ import annotations
88

9-
from collections.abc import Iterable, MutableMapping
109
from contextlib import suppress
11-
from re import Pattern
1210
from typing import TYPE_CHECKING, Any, Callable
1311

1412
from itemadapter import ItemAdapter
15-
from parsel import Selector
13+
from parsel import Selector # noqa: TC002 # for sphinx
1614
from parsel.utils import extract_regex, flatten
1715

1816
from itemloaders.common import wrap_loader_context
1917
from itemloaders.processors import Identity
2018
from itemloaders.utils import arg_to_iter
2119

2220
if TYPE_CHECKING:
21+
from collections.abc import Iterable, MutableMapping
22+
from re import Pattern
23+
2324
# typing.Self requires Python 3.11
2425
from typing_extensions import Self
2526

@@ -135,15 +136,13 @@ def __init__(
135136
def _values(self) -> dict[str, list[Any]]:
136137
if self.parent is not None:
137138
return self.parent._values
138-
else:
139-
return self._local_values
139+
return self._local_values
140140

141141
@property
142142
def item(self) -> Any:
143143
if self.parent is not None:
144144
return self.parent.item
145-
else:
146-
return self._local_item
145+
return self._local_item
147146

148147
def nested_xpath(self, xpath: str, **context: Any) -> Self:
149148
"""
@@ -157,8 +156,7 @@ def nested_xpath(self, xpath: str, **context: Any) -> Self:
157156
assert self.selector is not None
158157
selector = self.selector.xpath(xpath)
159158
context.update(selector=selector)
160-
subloader = self.__class__(item=self.item, parent=self, **context)
161-
return subloader
159+
return self.__class__(item=self.item, parent=self, **context)
162160

163161
def nested_css(self, css: str, **context: Any) -> Self:
164162
"""
@@ -172,8 +170,7 @@ def nested_css(self, css: str, **context: Any) -> Self:
172170
assert self.selector is not None
173171
selector = self.selector.css(css)
174172
context.update(selector=selector)
175-
subloader = self.__class__(item=self.item, parent=self, **context)
176-
return subloader
173+
return self.__class__(item=self.item, parent=self, **context)
177174

178175
def add_value(
179176
self,
@@ -288,13 +285,13 @@ def get_value(
288285
if value is None:
289286
break
290287
_proc = proc
291-
proc = wrap_loader_context(proc, self.context)
288+
proc = wrap_loader_context(proc, self.context) # noqa: PLW2901
292289
try:
293290
value = proc(value)
294291
except Exception as e:
295292
raise ValueError(
296-
"Error with processor %s value=%r error='%s: %s'"
297-
% (_proc.__class__.__name__, value, type(e).__name__, str(e))
293+
f"Error with processor {_proc.__class__.__name__} "
294+
f"value={value!r} error='{type(e).__name__}: {e!s}'"
298295
) from e
299296
return value
300297

@@ -324,24 +321,24 @@ def get_output_value(self, field_name: str) -> Any:
324321
return proc(value)
325322
except Exception as e:
326323
raise ValueError(
327-
"Error with output processor: field=%r value=%r error='%s: %s'"
328-
% (field_name, value, type(e).__name__, str(e))
324+
f"Error with output processor: field={field_name!r} "
325+
f"value={value!r} error='{type(e).__name__}: {e!s}'"
329326
) from e
330327

331328
def get_collected_values(self, field_name: str) -> list[Any]:
332329
"""Return the collected values for the given field."""
333330
return self._values.get(field_name, [])
334331

335332
def get_input_processor(self, field_name: str) -> Callable[..., Any]:
336-
proc = getattr(self, "%s_in" % field_name, None)
333+
proc = getattr(self, f"{field_name}_in", None)
337334
if not proc:
338335
proc = self._get_item_field_attr(
339336
field_name, "input_processor", self.default_input_processor
340337
)
341338
return unbound_method(proc)
342339

343340
def get_output_processor(self, field_name: str) -> Callable[..., Any]:
344-
proc = getattr(self, "%s_out" % field_name, None)
341+
proc = getattr(self, f"{field_name}_out", None)
345342
if not proc:
346343
proc = self._get_item_field_attr(
347344
field_name, "output_processor", self.default_output_processor
@@ -362,22 +359,15 @@ def _process_input_value(self, field_name: str, value: Any) -> Any:
362359
return proc(value)
363360
except Exception as e:
364361
raise ValueError(
365-
"Error with input processor %s: field=%r value=%r "
366-
"error='%s: %s'"
367-
% (
368-
_proc.__class__.__name__,
369-
field_name,
370-
value,
371-
type(e).__name__,
372-
str(e),
373-
)
362+
f"Error with input processor {_proc.__class__.__name__}:"
363+
f" field={field_name!r} value={value!r} error='{type(e).__name__}: {e!s}'"
374364
) from e
375365

376366
def _check_selector_method(self) -> None:
377367
if self.selector is None:
378368
raise RuntimeError(
379-
"To use XPath or CSS selectors, %s "
380-
"must be instantiated with a selector" % self.__class__.__name__
369+
f"To use XPath or CSS selectors, {self.__class__.__name__} "
370+
f"must be instantiated with a selector"
381371
)
382372

383373
def add_xpath(

itemloaders/common.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22

33
from __future__ import annotations
44

5-
from collections.abc import MutableMapping
65
from functools import partial
7-
from typing import Any, Callable
6+
from typing import TYPE_CHECKING, Any, Callable
87

98
from itemloaders.utils import get_func_args
109

10+
if TYPE_CHECKING:
11+
from collections.abc import MutableMapping
12+
1113

1214
def wrap_loader_context(
1315
function: Callable[..., Any], context: MutableMapping[str, Any]
@@ -17,5 +19,4 @@ def wrap_loader_context(
1719
"""
1820
if "loader_context" in get_func_args(function):
1921
return partial(function, loader_context=context)
20-
else:
21-
return function
22+
return function

itemloaders/processors.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@
77
from __future__ import annotations
88

99
from collections import ChainMap
10-
from collections.abc import Iterable, MutableMapping
11-
from typing import Any, Callable
10+
from typing import TYPE_CHECKING, Any, Callable
1211

1312
from itemloaders.common import wrap_loader_context
1413
from itemloaders.utils import arg_to_iter
1514

15+
if TYPE_CHECKING:
16+
from collections.abc import Iterable, MutableMapping
17+
1618

1719
class MapCompose:
1820
"""
@@ -56,7 +58,7 @@ class MapCompose:
5658
See :class:`Compose` processor for more info.
5759
5860
.. _`parsel selectors`: https://parsel.readthedocs.io/en/latest/parsel.html#parsel.selector.Selector.extract
59-
""" # noqa
61+
"""
6062

6163
def __init__(self, *functions: Callable[..., Any], **default_loader_context: Any):
6264
self.functions = functions
@@ -79,9 +81,8 @@ def __call__(
7981
next_values += arg_to_iter(func(v))
8082
except Exception as e:
8183
raise ValueError(
82-
"Error in MapCompose with "
83-
"%s value=%r error='%s: %s'"
84-
% (str(func), value, type(e).__name__, str(e))
84+
f"Error in MapCompose with "
85+
f"{func!s} value={value!r} error='{type(e).__name__}: {e!s}'"
8586
) from e
8687
values = next_values
8788
return values
@@ -137,9 +138,8 @@ def __call__(
137138
value = func(value)
138139
except Exception as e:
139140
raise ValueError(
140-
"Error in Compose with "
141-
"%s value=%r error='%s: %s'"
142-
% (str(func), value, type(e).__name__, str(e))
141+
f"Error in Compose with "
142+
f"{func!s} value={value!r} error='{type(e).__name__}: {e!s}'"
143143
) from e
144144
return value
145145

0 commit comments

Comments
 (0)