diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 5677e0a..119b328 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
- rev: v0.12.1
+ rev: v0.12.5
hooks:
- id: ruff-check
args: [ --fix ]
@@ -14,6 +14,7 @@ repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
+ - id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/sphinx-contrib/sphinx-lint
rev: v1.0.0
diff --git a/docs/conf.py b/docs/conf.py
index ba511bb..957107d 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -46,7 +46,7 @@
# General information about the project.
project = "itemloaders"
-copyright = "Zyte Group Ltd"
+project_copyright = "Zyte Group Ltd"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
diff --git a/pyproject.toml b/pyproject.toml
index 6e10f83..05da683 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -100,14 +100,21 @@ disable = [
"too-many-positional-arguments",
"too-many-public-methods",
"unused-argument",
+ "use-implicit-booleaness-not-comparison",
]
[tool.ruff.lint]
extend-select = [
+ # flake8-builtins
+ "A",
+ # flake8-async
+ "ASYNC",
# flake8-bugbear
"B",
# flake8-comprehensions
"C4",
+ # flake8-commas
+ "COM",
# pydocstyle
"D",
# flake8-future-annotations
@@ -130,6 +137,8 @@ extend-select = [
"PIE",
# pylint
"PL",
+ # flake8-pytest-style
+ "PT",
# flake8-use-pathlib
"PTH",
# flake8-pyi
@@ -160,6 +169,8 @@ extend-select = [
"YTT",
]
ignore = [
+ # Trailing comma missing
+ "COM812",
# Missing docstring in public module
"D100",
# Missing docstring in public class
@@ -218,5 +229,8 @@ ignore = [
"S101",
]
+[tool.ruff.lint.isort]
+split-on-trailing-comma = false
+
[tool.ruff.lint.pydocstyle]
convention = "pep257"
diff --git a/tests/test_base_loader.py b/tests/test_base_loader.py
index 5342d0c..a12e827 100644
--- a/tests/test_base_loader.py
+++ b/tests/test_base_loader.py
@@ -1,6 +1,7 @@
-import unittest
from functools import partial
+import pytest
+
from itemloaders import ItemLoader
from itemloaders.processors import Compose, Identity, Join, MapCompose, TakeFirst
@@ -20,7 +21,7 @@ def processor_with_args(value, other=None, loader_context=None):
return value
-class BasicItemLoaderTest(unittest.TestCase):
+class TestItemLoaderBasic:
def test_load_item_using_default_loader(self):
i = {"summary": "lala"}
il = ItemLoader(item=i)
@@ -61,7 +62,7 @@ class MyLoader(ItemLoader):
assert il.load_item() == {"name": "", "price": 0.0}
il.replace_value("sku", [valid_fragment], re=sku_re)
- self.assertEqual(il.load_item()["sku"], "1234")
+ assert il.load_item()["sku"] == "1234"
def test_self_referencing_loader(self):
class MyLoader(ItemLoader):
@@ -112,44 +113,41 @@ def test_add_none(self):
def test_replace_value(self):
il = CustomItemLoader()
il.replace_value("name", "marta")
- self.assertEqual(il.get_collected_values("name"), ["Marta"])
- self.assertEqual(il.get_output_value("name"), ["Marta"])
+ assert il.get_collected_values("name") == ["Marta"]
+ assert il.get_output_value("name") == ["Marta"]
il.replace_value("name", "pepe")
- self.assertEqual(il.get_collected_values("name"), ["Pepe"])
- self.assertEqual(il.get_output_value("name"), ["Pepe"])
+ assert il.get_collected_values("name") == ["Pepe"]
+ assert il.get_output_value("name") == ["Pepe"]
il.replace_value(None, "Jim", lambda x: {"name": x})
- self.assertEqual(il.get_collected_values("name"), ["Jim"])
+ assert il.get_collected_values("name") == ["Jim"]
def test_replace_value_none(self):
il = CustomItemLoader()
il.replace_value("name", None)
- self.assertEqual(il.get_collected_values("name"), [])
+ assert il.get_collected_values("name") == []
il.replace_value("name", "marta")
- self.assertEqual(il.get_collected_values("name"), ["Marta"])
+ assert il.get_collected_values("name") == ["Marta"]
il.replace_value(
"name", None
) # when replacing with `None` nothing should happen
- self.assertEqual(il.get_collected_values("name"), ["Marta"])
+ assert il.get_collected_values("name") == ["Marta"]
def test_get_value(self):
il = ItemLoader()
- self.assertEqual("FOO", il.get_value(["foo", "bar"], TakeFirst(), str.upper))
- self.assertEqual(
- ["foo", "bar"], il.get_value(["name:foo", "name:bar"], re="name:(.*)$")
- )
- self.assertEqual(
- "foo", il.get_value(["name:foo", "name:bar"], TakeFirst(), re="name:(.*)$")
- )
- self.assertEqual(
- None, il.get_value(["foo", "bar"], TakeFirst(), re="name:(.*)$")
+ assert il.get_value(["foo", "bar"], TakeFirst(), str.upper) == "FOO"
+ assert il.get_value(["name:foo", "name:bar"], re="name:(.*)$") == ["foo", "bar"]
+ assert (
+ il.get_value(["name:foo", "name:bar"], TakeFirst(), re="name:(.*)$")
+ == "foo"
)
- self.assertEqual(None, il.get_value(None, TakeFirst()))
+ assert None is il.get_value(["foo", "bar"], TakeFirst(), re="name:(.*)$")
+ assert None is il.get_value(None, TakeFirst())
il.add_value("name", ["name:foo", "name:bar"], TakeFirst(), re="name:(.*)$")
- self.assertEqual(["foo"], il.get_collected_values("name"))
+ assert il.get_collected_values("name") == ["foo"]
il.replace_value("name", "name:bar", re="name:(.*)$")
- self.assertEqual(["bar"], il.get_collected_values("name"))
+ assert il.get_collected_values("name") == ["bar"]
def test_iter_on_input_processor_input(self):
class NameFirstItemLoader(ItemLoader):
@@ -157,32 +155,34 @@ class NameFirstItemLoader(ItemLoader):
il = NameFirstItemLoader()
il.add_value("name", "marta")
- self.assertEqual(il.get_collected_values("name"), ["marta"])
+ assert il.get_collected_values("name") == ["marta"]
il = NameFirstItemLoader()
il.add_value("name", ["marta", "jose"])
- self.assertEqual(il.get_collected_values("name"), ["marta"])
+ assert il.get_collected_values("name") == ["marta"]
il = NameFirstItemLoader()
il.replace_value("name", "marta")
- self.assertEqual(il.get_collected_values("name"), ["marta"])
+ assert il.get_collected_values("name") == ["marta"]
il = NameFirstItemLoader()
il.replace_value("name", ["marta", "jose"])
- self.assertEqual(il.get_collected_values("name"), ["marta"])
+ assert il.get_collected_values("name") == ["marta"]
il = NameFirstItemLoader()
il.add_value("name", "marta")
il.add_value("name", ["jose", "pedro"])
- self.assertEqual(il.get_collected_values("name"), ["marta", "jose"])
+ assert il.get_collected_values("name") == ["marta", "jose"]
def test_map_compose_filter(self):
def filter_world(x):
return None if x == "world" else x
proc = MapCompose(filter_world, str.upper)
- self.assertEqual(
- proc(["hello", "world", "this", "is", "scrapy"]),
- ["HELLO", "THIS", "IS", "SCRAPY"],
- )
+ assert proc(["hello", "world", "this", "is", "scrapy"]) == [
+ "HELLO",
+ "THIS",
+ "IS",
+ "SCRAPY",
+ ]
def test_map_compose_filter_multil(self):
class CustomItemLoader(ItemLoader):
@@ -190,14 +190,14 @@ class CustomItemLoader(ItemLoader):
il = CustomItemLoader()
il.add_value("name", "marta")
- self.assertEqual(il.get_output_value("name"), ["Mart"])
+ assert il.get_output_value("name") == ["Mart"]
item = il.load_item()
- self.assertEqual(item["name"], ["Mart"])
+ assert item["name"] == ["Mart"]
def test_default_input_processor(self):
il = DefaultedItemLoader()
il.add_value("name", "marta")
- self.assertEqual(il.get_output_value("name"), ["mart"])
+ assert il.get_output_value("name") == ["mart"]
def test_inherited_default_input_processor(self):
class InheritDefaultedItemLoader(DefaultedItemLoader):
@@ -205,7 +205,7 @@ class InheritDefaultedItemLoader(DefaultedItemLoader):
il = InheritDefaultedItemLoader()
il.add_value("name", "marta")
- self.assertEqual(il.get_output_value("name"), ["mart"])
+ assert il.get_output_value("name") == ["mart"]
def test_input_processor_inheritance(self):
class ChildItemLoader(CustomItemLoader):
@@ -213,9 +213,9 @@ class ChildItemLoader(CustomItemLoader):
il = ChildItemLoader()
il.add_value("url", "HTTP://scrapy.ORG")
- self.assertEqual(il.get_output_value("url"), ["http://scrapy.org"])
+ assert il.get_output_value("url") == ["http://scrapy.org"]
il.add_value("name", "marta")
- self.assertEqual(il.get_output_value("name"), ["Marta"])
+ assert il.get_output_value("name") == ["Marta"]
class ChildChildItemLoader(ChildItemLoader):
url_in = MapCompose(lambda v: v.upper())
@@ -223,9 +223,9 @@ class ChildChildItemLoader(ChildItemLoader):
il = ChildChildItemLoader()
il.add_value("url", "http://scrapy.org")
- self.assertEqual(il.get_output_value("url"), ["HTTP://SCRAPY.ORG"])
+ assert il.get_output_value("url") == ["HTTP://SCRAPY.ORG"]
il.add_value("name", "marta")
- self.assertEqual(il.get_output_value("name"), ["Marta"])
+ assert il.get_output_value("name") == ["Marta"]
def test_empty_map_compose(self):
class IdentityDefaultedItemLoader(DefaultedItemLoader):
@@ -233,7 +233,7 @@ class IdentityDefaultedItemLoader(DefaultedItemLoader):
il = IdentityDefaultedItemLoader()
il.add_value("name", "marta")
- self.assertEqual(il.get_output_value("name"), ["marta"])
+ assert il.get_output_value("name") == ["marta"]
def test_identity_input_processor(self):
class IdentityDefaultedItemLoader(DefaultedItemLoader):
@@ -241,7 +241,7 @@ class IdentityDefaultedItemLoader(DefaultedItemLoader):
il = IdentityDefaultedItemLoader()
il.add_value("name", "marta")
- self.assertEqual(il.get_output_value("name"), ["marta"])
+ assert il.get_output_value("name") == ["marta"]
def test_extend_custom_input_processors(self):
class ChildItemLoader(CustomItemLoader):
@@ -249,7 +249,7 @@ class ChildItemLoader(CustomItemLoader):
il = ChildItemLoader()
il.add_value("name", "marta")
- self.assertEqual(il.get_output_value("name"), ["mARTA"])
+ assert il.get_output_value("name") == ["mARTA"]
def test_extend_default_input_processors(self):
class ChildDefaultedItemLoader(DefaultedItemLoader):
@@ -259,19 +259,19 @@ class ChildDefaultedItemLoader(DefaultedItemLoader):
il = ChildDefaultedItemLoader()
il.add_value("name", "marta")
- self.assertEqual(il.get_output_value("name"), ["MART"])
+ assert il.get_output_value("name") == ["MART"]
def test_output_processor_using_function(self):
il = CustomItemLoader()
il.add_value("name", ["mar", "ta"])
- self.assertEqual(il.get_output_value("name"), ["Mar", "Ta"])
+ assert il.get_output_value("name") == ["Mar", "Ta"]
class TakeFirstItemLoader(CustomItemLoader):
name_out = " ".join
il = TakeFirstItemLoader()
il.add_value("name", ["mar", "ta"])
- self.assertEqual(il.get_output_value("name"), "Mar Ta")
+ assert il.get_output_value("name") == "Mar Ta"
def test_output_processor_error(self):
class CustomItemLoader(ItemLoader):
@@ -300,33 +300,33 @@ class CustomItemLoader(ItemLoader):
def test_output_processor_using_classes(self):
il = CustomItemLoader()
il.add_value("name", ["mar", "ta"])
- self.assertEqual(il.get_output_value("name"), ["Mar", "Ta"])
+ assert il.get_output_value("name") == ["Mar", "Ta"]
class TakeFirstItemLoader1(CustomItemLoader):
name_out = Join()
il = TakeFirstItemLoader1()
il.add_value("name", ["mar", "ta"])
- self.assertEqual(il.get_output_value("name"), "Mar Ta")
+ assert il.get_output_value("name") == "Mar Ta"
class TakeFirstItemLoader2(CustomItemLoader):
name_out = Join("
")
il = TakeFirstItemLoader2()
il.add_value("name", ["mar", "ta"])
- self.assertEqual(il.get_output_value("name"), "Mar
Ta")
+ assert il.get_output_value("name") == "Mar
Ta"
def test_default_output_processor(self):
il = CustomItemLoader()
il.add_value("name", ["mar", "ta"])
- self.assertEqual(il.get_output_value("name"), ["Mar", "Ta"])
+ assert il.get_output_value("name") == ["Mar", "Ta"]
class LalaItemLoader(CustomItemLoader):
default_output_processor = Identity()
il = LalaItemLoader()
il.add_value("name", ["mar", "ta"])
- self.assertEqual(il.get_output_value("name"), ["Mar", "Ta"])
+ assert il.get_output_value("name") == ["Mar", "Ta"]
def test_loader_context_on_declaration(self):
class ChildItemLoader(CustomItemLoader):
@@ -334,9 +334,9 @@ class ChildItemLoader(CustomItemLoader):
il = ChildItemLoader()
il.add_value("url", "text")
- self.assertEqual(il.get_output_value("url"), ["val"])
+ assert il.get_output_value("url") == ["val"]
il.replace_value("url", "text2")
- self.assertEqual(il.get_output_value("url"), ["val"])
+ assert il.get_output_value("url") == ["val"]
def test_loader_context_on_instantiation(self):
class ChildItemLoader(CustomItemLoader):
@@ -344,9 +344,9 @@ class ChildItemLoader(CustomItemLoader):
il = ChildItemLoader(key="val")
il.add_value("url", "text")
- self.assertEqual(il.get_output_value("url"), ["val"])
+ assert il.get_output_value("url") == ["val"]
il.replace_value("url", "text2")
- self.assertEqual(il.get_output_value("url"), ["val"])
+ assert il.get_output_value("url") == ["val"]
def test_loader_context_on_assign(self):
class ChildItemLoader(CustomItemLoader):
@@ -355,9 +355,9 @@ class ChildItemLoader(CustomItemLoader):
il = ChildItemLoader()
il.context["key"] = "val"
il.add_value("url", "text")
- self.assertEqual(il.get_output_value("url"), ["val"])
+ assert il.get_output_value("url") == ["val"]
il.replace_value("url", "text2")
- self.assertEqual(il.get_output_value("url"), ["val"])
+ assert il.get_output_value("url") == ["val"]
def test_item_passed_to_input_processor_functions(self):
def processor(value, loader_context):
@@ -369,9 +369,9 @@ class ChildItemLoader(CustomItemLoader):
it = {"name": "marta"}
il = ChildItemLoader(item=it)
il.add_value("url", "text")
- self.assertEqual(il.get_output_value("url"), ["marta"])
+ assert il.get_output_value("url") == ["marta"]
il.replace_value("url", "text2")
- self.assertEqual(il.get_output_value("url"), ["marta"])
+ assert il.get_output_value("url") == ["marta"]
# def test_add_value_on_unknown_field(self):
# il = CustomItemLoader()
@@ -383,9 +383,9 @@ class CustomItemLoader(ItemLoader):
il = CustomItemLoader()
il.add_value("name", ["marta", "other"])
- self.assertEqual(il.get_output_value("name"), "Mart")
+ assert il.get_output_value("name") == "Mart"
item = il.load_item()
- self.assertEqual(item["name"], "Mart")
+ assert item["name"] == "Mart"
def test_partial_processor(self):
def join(values, sep=None, loader_context=None, ignored=None):
@@ -405,16 +405,22 @@ class CustomItemLoader(ItemLoader):
il.add_value("url", ["rabbit", "hole"])
il.add_value("summary", ["rabbit", "hole"])
item = il.load_item()
- self.assertEqual(item["name"], "rabbit+hole")
- self.assertEqual(item["url"], "rabbit.hole")
- self.assertEqual(item["summary"], "rabbithole")
+ assert item["name"] == "rabbit+hole"
+ assert item["url"] == "rabbit.hole"
+ assert item["summary"] == "rabbithole"
def test_error_input_processor(self):
class CustomItemLoader(ItemLoader):
name_in = MapCompose(float)
il = CustomItemLoader()
- self.assertRaises(ValueError, il.add_value, "name", ["marta", "other"])
+ with pytest.raises(
+ ValueError,
+ match="Error with input processor MapCompose: .* "
+ "error='ValueError: Error in MapCompose .* "
+ "error='ValueError: could not convert",
+ ):
+ il.add_value("name", ["marta", "other"])
def test_error_output_processor(self):
class CustomItemLoader(ItemLoader):
@@ -422,20 +428,29 @@ class CustomItemLoader(ItemLoader):
il = CustomItemLoader()
il.add_value("name", "marta")
- with self.assertRaises(ValueError):
+ with pytest.raises(
+ ValueError,
+ match="Error with output processor: .* "
+ "error='ValueError: Error in Compose .* "
+ "error='ValueError: could not convert",
+ ):
il.load_item()
def test_error_processor_as_argument(self):
il = CustomItemLoader()
- self.assertRaises(
- ValueError, il.add_value, "name", ["marta", "other"], Compose(float)
- )
+ with pytest.raises(
+ ValueError,
+ match=r"Error with processor Compose .* "
+ r"error='ValueError: Error in Compose .* "
+ r"error='TypeError: float\(\) argument",
+ ):
+ il.add_value("name", ["marta", "other"], Compose(float))
def test_get_unset_value(self):
loader = ItemLoader()
- self.assertEqual(loader.load_item(), {})
- self.assertEqual(loader.get_output_value("foo"), [])
- self.assertEqual(loader.load_item(), {})
+ assert loader.load_item() == {}
+ assert loader.get_output_value("foo") == []
+ assert loader.load_item() == {}
class BaseNoInputReprocessingLoader(ItemLoader):
@@ -447,7 +462,7 @@ class NoInputReprocessingDictLoader(BaseNoInputReprocessingLoader):
default_item_class = dict
-class NoInputReprocessingFromDictTest(unittest.TestCase):
+class TestNoInputReprocessingFromDict:
"""
Loaders initialized from loaded items must not reprocess fields (dict instances)
"""
@@ -455,33 +470,33 @@ class NoInputReprocessingFromDictTest(unittest.TestCase):
def test_avoid_reprocessing_with_initial_values_single(self):
il = NoInputReprocessingDictLoader(item={"title": "foo"})
il_loaded = il.load_item()
- self.assertEqual(il_loaded, {"title": "foo"})
- self.assertEqual(
- NoInputReprocessingDictLoader(item=il_loaded).load_item(), {"title": "foo"}
- )
+ assert il_loaded == {"title": "foo"}
+ assert NoInputReprocessingDictLoader(item=il_loaded).load_item() == {
+ "title": "foo"
+ }
def test_avoid_reprocessing_with_initial_values_list(self):
il = NoInputReprocessingDictLoader(item={"title": ["foo", "bar"]})
il_loaded = il.load_item()
- self.assertEqual(il_loaded, {"title": "foo"})
- self.assertEqual(
- NoInputReprocessingDictLoader(item=il_loaded).load_item(), {"title": "foo"}
- )
+ assert il_loaded == {"title": "foo"}
+ assert NoInputReprocessingDictLoader(item=il_loaded).load_item() == {
+ "title": "foo"
+ }
def test_avoid_reprocessing_without_initial_values_single(self):
il = NoInputReprocessingDictLoader()
il.add_value("title", "foo")
il_loaded = il.load_item()
- self.assertEqual(il_loaded, {"title": "FOO"})
- self.assertEqual(
- NoInputReprocessingDictLoader(item=il_loaded).load_item(), {"title": "FOO"}
- )
+ assert il_loaded == {"title": "FOO"}
+ assert NoInputReprocessingDictLoader(item=il_loaded).load_item() == {
+ "title": "FOO"
+ }
def test_avoid_reprocessing_without_initial_values_list(self):
il = NoInputReprocessingDictLoader()
il.add_value("title", ["foo", "bar"])
il_loaded = il.load_item()
- self.assertEqual(il_loaded, {"title": "FOO"})
- self.assertEqual(
- NoInputReprocessingDictLoader(item=il_loaded).load_item(), {"title": "FOO"}
- )
+ assert il_loaded == {"title": "FOO"}
+ assert NoInputReprocessingDictLoader(item=il_loaded).load_item() == {
+ "title": "FOO"
+ }
diff --git a/tests/test_loader_initialization.py b/tests/test_loader_initialization.py
index b2ba331..b609904 100644
--- a/tests/test_loader_initialization.py
+++ b/tests/test_loader_initialization.py
@@ -1,106 +1,101 @@
from __future__ import annotations
-import unittest
-from typing import Any, Protocol
+from abc import ABC, abstractmethod
+from typing import Any
from itemloaders import ItemLoader
-class InitializationTestProtocol(Protocol):
- item_class: Any
+class TestInitializationBase(ABC):
+ @property
+ @abstractmethod
+ def item_class(self) -> type[Any]:
+ raise NotImplementedError
- def assertEqual(self, first: Any, second: Any, msg: Any = ...) -> None: ...
-
- def assertIsInstance(self, obj: object, cls: type, msg: Any = None) -> None: ...
-
-
-class InitializationTestMixin:
- item_class: Any = None
-
- def test_keep_single_value(self: InitializationTestProtocol) -> None:
+ def test_keep_single_value(self) -> None:
"""Loaded item should contain values from the initial item"""
input_item = self.item_class(name="foo")
il = ItemLoader(item=input_item)
loaded_item = il.load_item()
- self.assertIsInstance(loaded_item, self.item_class)
- self.assertEqual(dict(loaded_item), {"name": ["foo"]})
+ assert isinstance(loaded_item, self.item_class)
+ assert dict(loaded_item) == {"name": ["foo"]}
- def test_keep_list(self: InitializationTestProtocol) -> None:
+ def test_keep_list(self) -> None:
"""Loaded item should contain values from the initial item"""
input_item = self.item_class(name=["foo", "bar"])
il = ItemLoader(item=input_item)
loaded_item = il.load_item()
- self.assertIsInstance(loaded_item, self.item_class)
- self.assertEqual(dict(loaded_item), {"name": ["foo", "bar"]})
+ assert isinstance(loaded_item, self.item_class)
+ assert dict(loaded_item) == {"name": ["foo", "bar"]}
def test_add_value_singlevalue_singlevalue(
- self: InitializationTestProtocol,
+ self,
) -> None:
"""Values added after initialization should be appended"""
input_item = self.item_class(name="foo")
il = ItemLoader(item=input_item)
il.add_value("name", "bar")
loaded_item = il.load_item()
- self.assertIsInstance(loaded_item, self.item_class)
- self.assertEqual(dict(loaded_item), {"name": ["foo", "bar"]})
+ assert isinstance(loaded_item, self.item_class)
+ assert dict(loaded_item) == {"name": ["foo", "bar"]}
- def test_add_value_singlevalue_list(self: InitializationTestProtocol) -> None:
+ def test_add_value_singlevalue_list(self) -> None:
"""Values added after initialization should be appended"""
input_item = self.item_class(name="foo")
il = ItemLoader(item=input_item)
il.add_value("name", ["item", "loader"])
loaded_item = il.load_item()
- self.assertIsInstance(loaded_item, self.item_class)
- self.assertEqual(dict(loaded_item), {"name": ["foo", "item", "loader"]})
+ assert isinstance(loaded_item, self.item_class)
+ assert dict(loaded_item) == {"name": ["foo", "item", "loader"]}
- def test_add_value_list_singlevalue(self: InitializationTestProtocol) -> None:
+ def test_add_value_list_singlevalue(self) -> None:
"""Values added after initialization should be appended"""
input_item = self.item_class(name=["foo", "bar"])
il = ItemLoader(item=input_item)
il.add_value("name", "qwerty")
loaded_item = il.load_item()
- self.assertIsInstance(loaded_item, self.item_class)
- self.assertEqual(dict(loaded_item), {"name": ["foo", "bar", "qwerty"]})
+ assert isinstance(loaded_item, self.item_class)
+ assert dict(loaded_item) == {"name": ["foo", "bar", "qwerty"]}
- def test_add_value_list_list(self: InitializationTestProtocol) -> None:
+ def test_add_value_list_list(self) -> None:
"""Values added after initialization should be appended"""
input_item = self.item_class(name=["foo", "bar"])
il = ItemLoader(item=input_item)
il.add_value("name", ["item", "loader"])
loaded_item = il.load_item()
- self.assertIsInstance(loaded_item, self.item_class)
- self.assertEqual(dict(loaded_item), {"name": ["foo", "bar", "item", "loader"]})
+ assert isinstance(loaded_item, self.item_class)
+ assert dict(loaded_item) == {"name": ["foo", "bar", "item", "loader"]}
- def test_get_output_value_singlevalue(self: InitializationTestProtocol) -> None:
+ def test_get_output_value_singlevalue(self) -> None:
"""Getting output value must not remove value from item"""
input_item = self.item_class(name="foo")
il = ItemLoader(item=input_item)
- self.assertEqual(il.get_output_value("name"), ["foo"])
+ assert il.get_output_value("name") == ["foo"]
loaded_item = il.load_item()
- self.assertIsInstance(loaded_item, self.item_class)
- self.assertEqual(loaded_item, {"name": ["foo"]})
+ assert isinstance(loaded_item, self.item_class)
+ assert loaded_item == {"name": ["foo"]}
- def test_get_output_value_list(self: InitializationTestProtocol) -> None:
+ def test_get_output_value_list(self) -> None:
"""Getting output value must not remove value from item"""
input_item = self.item_class(name=["foo", "bar"])
il = ItemLoader(item=input_item)
- self.assertEqual(il.get_output_value("name"), ["foo", "bar"])
+ assert il.get_output_value("name") == ["foo", "bar"]
loaded_item = il.load_item()
- self.assertIsInstance(loaded_item, self.item_class)
- self.assertEqual(loaded_item, {"name": ["foo", "bar"]})
+ assert isinstance(loaded_item, self.item_class)
+ assert loaded_item == {"name": ["foo", "bar"]}
- def test_values_single(self: InitializationTestProtocol) -> None:
+ def test_values_single(self) -> None:
"""Values from initial item must be added to loader._values"""
input_item = self.item_class(name="foo")
il = ItemLoader(item=input_item)
- self.assertEqual(il._values.get("name"), ["foo"])
+ assert il._values.get("name") == ["foo"]
- def test_values_list(self: InitializationTestProtocol) -> None:
+ def test_values_list(self) -> None:
"""Values from initial item must be added to loader._values"""
input_item = self.item_class(name=["foo", "bar"])
il = ItemLoader(item=input_item)
- self.assertEqual(il._values.get("name"), ["foo", "bar"])
+ assert il._values.get("name") == ["foo", "bar"]
-class InitializationFromDictTest(InitializationTestMixin, unittest.TestCase):
+class InitializationFromDictTest(TestInitializationBase):
item_class = dict
diff --git a/tests/test_nested_items.py b/tests/test_nested_items.py
index f780ec2..010cbad 100644
--- a/tests/test_nested_items.py
+++ b/tests/test_nested_items.py
@@ -1,53 +1,55 @@
from __future__ import annotations
-import unittest
from typing import Any
+import pytest
+
from itemloaders import ItemLoader
-class NestedItemTest(unittest.TestCase):
- """Test that adding items as values works as expected."""
+def _test_item(item: Any) -> None:
+ il = ItemLoader()
+ il.add_value("item_list", item)
+ assert il.load_item() == {"item_list": [item]}
+
+
+def test_attrs():
+ try:
+ import attr # noqa: PLC0415
+ except ImportError:
+ pytest.skip("Cannot import attr")
+
+ @attr.s
+ class TestItem:
+ foo = attr.ib()
- def _test_item(self, item: Any) -> None:
- il = ItemLoader()
- il.add_value("item_list", item)
- self.assertEqual(il.load_item(), {"item_list": [item]})
+ _test_item(TestItem(foo="bar"))
- def test_attrs(self):
- try:
- import attr # noqa: PLC0415
- except ImportError:
- self.skipTest("Cannot import attr")
- @attr.s
- class TestItem:
- foo = attr.ib()
+def test_dataclass():
+ try:
+ from dataclasses import dataclass # noqa: PLC0415
+ except ImportError:
+ pytest.skip("Cannot import dataclasses.dataclass")
- self._test_item(TestItem(foo="bar"))
+ @dataclass
+ class TestItem:
+ foo: str
- def test_dataclass(self):
- try:
- from dataclasses import dataclass # noqa: PLC0415
- except ImportError:
- self.skipTest("Cannot import dataclasses.dataclass")
+ _test_item(TestItem(foo="bar"))
- @dataclass
- class TestItem:
- foo: str
- self._test_item(TestItem(foo="bar"))
+def test_dict():
+ _test_item({"foo": "bar"})
- def test_dict(self):
- self._test_item({"foo": "bar"})
- def test_scrapy_item(self):
- try:
- from scrapy import Field, Item # noqa: PLC0415
- except ImportError:
- self.skipTest("Cannot import Field or Item from scrapy")
+def test_scrapy_item():
+ try:
+ from scrapy import Field, Item # noqa: PLC0415
+ except ImportError:
+ pytest.skip("Cannot import Field or Item from scrapy")
- class TestItem(Item):
- foo = Field()
+ class TestItem(Item):
+ foo = Field()
- self._test_item(TestItem(foo="bar"))
+ _test_item(TestItem(foo="bar"))
diff --git a/tests/test_nested_loader.py b/tests/test_nested_loader.py
index 19e4bd3..407d9fe 100644
--- a/tests/test_nested_loader.py
+++ b/tests/test_nested_loader.py
@@ -1,11 +1,9 @@
-import unittest
-
from parsel import Selector
from itemloaders import ItemLoader
-class SubselectorLoaderTest(unittest.TestCase):
+class TestSubselectorLoader:
selector = Selector(
text="""
@@ -31,18 +29,14 @@ def test_nested_xpath(self):
assert nl.selector
nl.add_value("name_value", nl.selector.xpath('div[@id = "id"]/text()').getall())
- self.assertEqual(loader.get_output_value("name"), ["marta"])
- self.assertEqual(
- loader.get_output_value("name_div"), ['