Repository: scrapy/parsel

Issue:
Allow bytearray to be used as a body parameter for the Selector
Since `bytearray` and `bytes` are equivalent, we should support both. I recently encountered a problem in one of my pipelines where passing a `bytearray` made the Selector fail. The solution was to explicitly convert it to bytes, _e.g:_ `Selector(body=bytes(content))`
```
  File "/Users/laerte/Projects/dummy/venv/lib/python3.10/site-packages/parsel/selector.py", line 507, in __init__
    raise TypeError(msg)
TypeError: body argument should be of type bytes, got <class 'bytearray'>
```
Example:
```python
content = bytearray("<h1>Hello World</h1>", "utf-8")

from parsel import Selector

selector = Selector(body=bytes(content))
print(selector.xpath("//h1").get())
```

Discussion:

Solution patch:
diff --git a/parsel/selector.py b/parsel/selector.py
index 28cd1e9..01d06a5 100644
--- a/parsel/selector.py
+++ b/parsel/selector.py
@@ -424,7 +424,7 @@ def __init__(
         self,
         text: str | None = None,
         type: str | None = None,
-        body: bytes = b"",
+        body: bytes | bytearray = b"",
         encoding: str = "utf-8",
         namespaces: Mapping[str, str] | None = None,
         root: Any | None = _NOT_SET,
@@ -462,11 +462,11 @@ def __init__(
             self.root = root
             self.type = type
         elif body:
-            if not isinstance(body, bytes):
-                msg = f"body argument should be of type bytes, got {body.__class__}"
+            if not isinstance(body, (bytes, bytearray)):
+                msg = f"body argument should be of type bytes or bytearray, got {body.__class__}"
                 raise TypeError(msg)
             root, type = _get_root_and_type_from_bytes(
-                body=body,
+                body=bytes(body),
                 encoding=encoding,
                 input_type=type,
                 base_url=base_url,


Test patch:
diff --git a/tests/test_selector.py b/tests/test_selector.py
index a3b1cb8..75beae0 100644
--- a/tests/test_selector.py
+++ b/tests/test_selector.py
@@ -1200,6 +1200,10 @@ def test_json_selector_representation(self) -> None:
         assert repr(selector) == "<Selector query=None data='1'>"
         assert str(selector) == "1"
 
+    def test_body_bytearray_support(self) -> None:
+        selector = Selector(body=bytearray("<h1>Hello World</h1>", "utf-8"))
+        assert selector.xpath("//h1/text()").get() == "Hello World"
+
 
 class ExsltTestCase(unittest.TestCase):
     sscls = Selector