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:

Resolving patch:
diff --git a/scrapy/exporters.py b/scrapy/exporters.py
--- a/scrapy/exporters.py
+++ b/scrapy/exporters.py
@@ -7,13 +7,11 @@
 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 @@ def _serialize_value(self, value):
             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
--- a/tests/test_exporters.py
+++ b/tests/test_exporters.py
@@ -7,12 +7,10 @@
 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 @@ def test_export_item_dict_list(self):
         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()