Repository: scrapy/scrapy

Issue:
Remove deprecated PythonItemExporter.binary
The binary mode of `PythonItemExporter` was deprecated in Scrapy 1.1.0 but we've missed it in previous deprecation removal rounds.

Discussion:
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/scrapy/scrapy/pull/6007?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=scrapy) Report
> Merging [#6007](https://app.codecov.io/gh/scrapy/scrapy/pull/6007?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=scrapy) (9a5f9d2) into [master](https://app.codecov.io/gh/scrapy/scrapy/commit/85696d7bab3ac4e7180251a8def3aee6a56999a1?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=scrapy) (85696d7) will **decrease** coverage by `46.41%`.
> The diff coverage is `0.00%`.

> :exclamation: Current head 9a5f9d2 differs from pull request most recent head 04b6529. Consider uploading reports for the commit 04b6529 to get more accurate results

```diff
@@             Coverage Diff             @@
##           master    #6007       +/-   ##
===========================================
- Coverage   89.08%   42.67%   -46.41%     
===========================================
  Files         162      162               
  Lines       11425    11416        -9     
  Branches     1856     1852        -4     
===========================================
- Hits        10178     4872     -5306     
- Misses        949     6184     +5235     
- Partials      298      360       +62     
```

| [Files Changed](https://app.codecov.io/gh/scrapy/scrapy/pull/6007?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=scrapy) | Coverage Δ | |
|---|---|---|
| [scrapy/exporters.py](https://app.codecov.io/gh/scrapy/scrapy/pull/6007?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=scrapy#diff-c2NyYXB5L2V4cG9ydGVycy5weQ==) | `40.69% <0.00%> (-59.31%)` | :arrow_down: |

... and [141 files with indirect coverage changes](https://app.codecov.io/gh/scrapy/scrapy/pull/6007/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=scrapy)


Patch:
diff --git a/scrapy/exporters.py b/scrapy/exporters.py
index 8254ea63e..f85f1dad8 100644
--- a/scrapy/exporters.py
+++ b/scrapy/exporters.py
@@ -7,13 +7,11 @@ import io
 import marshal
 import pickle
 import pprint
-import warnings
 from collections.abc import Mapping
 from xml.sax.saxutils import XMLGenerator
 
 from itemadapter import ItemAdapter, is_item
 
-from scrapy.exceptions import ScrapyDeprecationWarning
 from scrapy.item import Item
 from scrapy.utils.python import is_listlike, to_bytes, to_unicode
 from scrapy.utils.serialize import ScrapyJSONEncoder
@@ -330,13 +328,7 @@ class PythonItemExporter(BaseItemExporter):
     """
 
     def _configure(self, options, dont_fail=False):
-        self.binary = options.pop("binary", True)
         super()._configure(options, dont_fail)
-        if self.binary:
-            warnings.warn(
-                "PythonItemExporter will drop support for binary export in the future",
-                ScrapyDeprecationWarning,
-            )
         if not self.encoding:
             self.encoding = "utf-8"
 
@@ -351,18 +343,14 @@ class PythonItemExporter(BaseItemExporter):
             return dict(self._serialize_item(value))
         if is_listlike(value):
             return [self._serialize_value(v) for v in value]
-        encode_func = to_bytes if self.binary else to_unicode
         if isinstance(value, (str, bytes)):
-            return encode_func(value, encoding=self.encoding)
+            return to_unicode(value, encoding=self.encoding)
         return value
 
     def _serialize_item(self, item):
         for key, value in ItemAdapter(item).items():
-            key = to_bytes(key) if self.binary else key
             yield key, self._serialize_value(value)
 
     def export_item(self, item):
         result = dict(self._get_serialized_fields(item))
-        if self.binary:
-            result = dict(self._serialize_item(result))
         return result


Test patch:
diff --git a/tests/test_exporters.py b/tests/test_exporters.py
index cb24ddd8e..f4e82705a 100644
--- a/tests/test_exporters.py
+++ b/tests/test_exporters.py
@@ -7,12 +7,10 @@ import tempfile
 import unittest
 from datetime import datetime
 from io import BytesIO
-from warnings import catch_warnings, filterwarnings
 
 import lxml.etree
 from itemadapter import ItemAdapter
 
-from scrapy.exceptions import ScrapyDeprecationWarning
 from scrapy.exporters import (
     BaseItemExporter,
     CsvItemExporter,
@@ -143,7 +141,7 @@ class BaseItemExporterDataclassTest(BaseItemExporterTest):
 
 class PythonItemExporterTest(BaseItemExporterTest):
     def _get_exporter(self, **kwargs):
-        return PythonItemExporter(binary=False, **kwargs)
+        return PythonItemExporter(**kwargs)
 
     def test_invalid_option(self):
         with self.assertRaisesRegex(TypeError, "Unexpected options: invalid_option"):
@@ -198,14 +196,6 @@ class PythonItemExporterTest(BaseItemExporterTest):
         self.assertEqual(type(exported["age"][0]), dict)
         self.assertEqual(type(exported["age"][0]["age"][0]), dict)
 
-    def test_export_binary(self):
-        with catch_warnings():
-            filterwarnings("ignore", category=ScrapyDeprecationWarning)
-            exporter = PythonItemExporter(binary=True)
-            value = self.item_class(name="John\xa3", age="22")
-            expected = {b"name": b"John\xc2\xa3", b"age": b"22"}
-            self.assertEqual(expected, exporter.export_item(value))
-
     def test_nonstring_types_item(self):
         item = self._get_nonstring_types_item()
         ie = self._get_exporter()