rename some exporter methods and complete exporter tests refactoring

This commit is contained in:
Pablo Hoffman 2009-08-20 14:09:24 -03:00
parent 6b7162f3ca
commit 20e82335e2
4 changed files with 94 additions and 92 deletions

View File

@ -86,8 +86,8 @@ Example::
price = Field(serializer=serialize_price)
2. Overriding the serialize() method
------------------------------------
2. Overriding the serialize_field() method
------------------------------------------
You can also override the :meth:`~BaseItemExporter.serialize` method to
customize how your field value will be exported.
@ -101,10 +101,10 @@ Example::
class ProductXmlExporter(XmlItemExporter):
def serialize(self, field, name, value):
def serialize_field(self, field, name, value):
if filed == 'price':
return '$ %s' % str(value)
return super(Product, self).serialize(field, name, value)
return super(Product, self).serialize_field(field, name, value)
.. _topics-exporters-reference:
@ -129,10 +129,19 @@ BaseItemExporter
Exports the item to the specific exporter format. This method must be
implemented in subclasses.
.. method:: serialize_default(field, name, value)
.. method:: serialize_field(field, name, value)
Serializes the field value to ``str``. You can override this method in
custom Item Exporters.
Return the serialized value for the given field. You can override this
method (in your custom Item Exporters) if you want to control how a
particular field or value will be serialized/exported.
:param field: the field being serialized
:type field: :class:`~scrapy.item.Field` object
:param name: the name of the field being serialized
:type name: str
:param value: the value being serialized
.. method:: start_exporting()

View File

@ -21,12 +21,14 @@ class BaseItemExporter(object):
def export_item(self, item):
raise NotImplementedError
def serialize(self, field, name, value):
def serialize_field(self, field, name, value):
serializer = field.get('serializer', identity)
return serializer(value)
def _get_fields_to_export(self, item, default_value=None, include_empty=None):
"""Return the fields to export as a list of tuples (name, value)"""
def _get_serialized_fields(self, item, default_value=None, include_empty=None):
"""Return the fields to export as an iterable of tuples (name,
serialized_value)
"""
if include_empty is None:
include_empty = self.export_empty_fields
if self.fields_to_export is None:
@ -41,7 +43,14 @@ class BaseItemExporter(object):
nonempty_fields = set(item.keys())
field_iter = (x for x in self.fields_to_export if x in \
nonempty_fields)
return [(k, item.get(k, default_value)) for k in field_iter]
for field_name in field_iter:
if field_name in item:
field = item.fields[field_name]
value = self.serialize_field(field, field_name, item[field_name])
else:
value = default_value
yield field_name, value
def start_exporting(self):
pass
@ -66,18 +75,17 @@ class XmlItemExporter(BaseItemExporter):
def export_item(self, item):
self.xg.startElement(self.item_element, {})
for field, value in self._get_fields_to_export(item, default_value=''):
self._export_xml_field(item.fields[field], field, value)
for name, value in self._get_serialized_fields(item, default_value=''):
self._export_xml_field(name, value)
self.xg.endElement(self.item_element)
def finish_exporting(self):
self.xg.endElement(self.root_element)
self.xg.endDocument()
def _export_xml_field(self, field, name, value):
def _export_xml_field(self, name, serialized_value):
self.xg.startElement(name, {})
if value is not None:
self.xg.characters(self.serialize(field, name, value))
self.xg.characters(serialized_value)
self.xg.endElement(name)
@ -97,7 +105,7 @@ class CsvItemExporter(BaseItemExporter):
self.csv_writer.writerow(self.fields_to_export)
def export_item(self, item):
fields = self._get_fields_to_export(item, default_value='', \
fields = self._get_serialized_fields(item, default_value='', \
include_empty=True)
values = [x[1] for x in fields]
@ -111,7 +119,7 @@ class PickleItemExporter(BaseItemExporter):
self.pickler = Pickler(*args, **kwargs)
def export_item(self, item):
self.pickler.dump(dict(self._get_fields_to_export(item)))
self.pickler.dump(dict(self._get_serialized_fields(item)))
class PprintItemExporter(BaseItemExporter):
@ -121,5 +129,5 @@ class PprintItemExporter(BaseItemExporter):
self.file = file
def export_item(self, item):
itemdict = dict(self._get_fields_to_export(item))
itemdict = dict(self._get_serialized_fields(item))
self.file.write(pprint.pformat(itemdict) + '\n')

View File

@ -13,7 +13,5 @@ class JsonLinesItemExporter(BaseItemExporter):
self.encoder = json.JSONEncoder(*args, **kwargs)
def export_item(self, item):
itemdict = {}
for field, value in self._get_fields_to_export(item):
itemdict[field] = self.serialize(item.fields[field], field, value)
itemdict = dict(self._get_serialized_fields(item))
self.file.write(self.encoder.encode(itemdict) + '\n')

View File

@ -1,10 +1,9 @@
from cPickle import Pickler
import cPickle as pickle
from cStringIO import StringIO
from twisted.trial import unittest
from scrapy.item import Item, Field
from scrapy.contrib.exporter import BaseItemExporter, PprintItemExporter, \
PickleItemExporter, CsvItemExporter, XmlItemExporter
@ -13,39 +12,50 @@ class TestItem(Item):
age = Field()
class BaseTest(unittest.TestCase):
class BaseItemExporterTest(unittest.TestCase):
def setUp(self):
self.i = TestItem(name=u'John', age='22')
self.ie = BaseItemExporter()
self.output = StringIO()
self.ie = self._get_exporter()
class BaseItemExporterTest(BaseTest):
def test_export(self):
self.assertRaises(NotImplementedError, self.ie.export_item, self.i)
def _get_exporter(self):
return BaseItemExporter()
def test_serialize(self):
self.assertEqual(self.ie.serialize( \
def _check_output(self):
pass
def test_export_item(self):
self.ie.start_exporting()
try:
self.ie.export_item(self.i)
except NotImplementedError:
if self.ie.__class__ is not BaseItemExporter:
raise
self.ie.finish_exporting()
self._check_output()
def test_serialize_field(self):
self.assertEqual(self.ie.serialize_field( \
self.i.fields['name'], 'name', self.i['name']), 'John')
self.assertEqual( \
self.ie.serialize(self.i.fields['age'], 'age', self.i['age']), '22')
self.ie.serialize_field(self.i.fields['age'], 'age', self.i['age']), '22')
def test_exporter_custom_serializer(self):
class CustomItemExporter(BaseItemExporter):
def serialize(self, field, name, value):
def serialize_field(self, field, name, value):
if name == 'age':
return str(int(value) + 1)
else:
return super(CustomItemExporter, self).serialize(field, \
return super(CustomItemExporter, self).serialize_field(field, \
name, value)
ie = CustomItemExporter()
self.assertEqual( \
ie.serialize(self.i.fields['name'], 'name', self.i['name']), 'John')
ie.serialize_field(self.i.fields['name'], 'name', self.i['name']), 'John')
self.assertEqual(
ie.serialize(self.i.fields['age'], 'age', self.i['age']), '23')
ie.serialize_field(self.i.fields['age'], 'age', self.i['age']), '23')
def test_field_custom_serializer(self):
def custom_serializer(value):
@ -58,57 +68,44 @@ class BaseItemExporterTest(BaseTest):
i = CustomFieldItem(name=u'John', age='22')
self.assertEqual( \
self.ie.serialize(i.fields['name'], 'name', i['name']), 'John')
self.ie.serialize_field(i.fields['name'], 'name', i['name']), 'John')
self.assertEqual( \
self.ie.serialize(i.fields['age'], 'age', i['age']), '24')
self.ie.serialize_field(i.fields['age'], 'age', i['age']), '24')
def test_fields_to_export(self):
ie = BaseItemExporter()
ie.fields_to_export = ['name']
self.assertEqual(ie._get_fields_to_export(self.i), [('name', 'John')])
self.assertEqual(list(ie._get_serialized_fields(self.i)), [('name', 'John')])
class PprintItemExporterTest(BaseItemExporterTest):
def _get_exporter(self):
return PprintItemExporter(self.output)
def _check_output(self):
self.assertEqual(dict(self.i), eval(self.output.getvalue()))
class PprintItemExporterTest(BaseTest):
def test_export(self):
ie = PprintItemExporter(self.output)
ie.start_exporting()
ie.export_item(self.i)
ie.finish_exporting()
self.assertEqual(self.output.getvalue(), "{'age': '22', 'name': u'John'}\n")
class PickleItemExporterTest(BaseTest):
class PickleItemExporterTest(BaseItemExporterTest):
def test_export(self):
output = StringIO()
ie = PickleItemExporter(output)
def _get_exporter(self):
return PickleItemExporter(self.output)
ie.start_exporting()
ie.export_item(self.i)
ie.finish_exporting()
def _check_output(self):
self.assertEqual(dict(self.i), pickle.loads(self.output.getvalue()))
poutput = StringIO()
p = Pickler(poutput)
p.dump(dict(self.i))
self.assertEqual(output.getvalue(), poutput.getvalue())
class CsvItemExporterTest(BaseItemExporterTest):
def _get_exporter(self):
return CsvItemExporter(self.output)
class CsvItemExporterTest(BaseTest):
def test_export(self):
ie = CsvItemExporter(self.output)
ie.start_exporting()
ie.export_item(self.i)
ie.finish_exporting()
def _check_output(self):
self.assertEqual(self.output.getvalue(), '22,John\r\n')
def test_header(self):
ie = CsvItemExporter(self.output)
output = StringIO()
ie = CsvItemExporter(output)
ie.include_headers_line = True
self.assertRaises(RuntimeError, ie.start_exporting)
@ -118,25 +115,22 @@ class CsvItemExporterTest(BaseTest):
ie.export_item(self.i)
ie.finish_exporting()
self.assertEqual(self.output.getvalue(), 'age,name\r\n22,John\r\n')
self.assertEqual(output.getvalue(), 'age,name\r\n22,John\r\n')
class XmlItemExporterTest(BaseTest):
class XmlItemExporterTest(BaseItemExporterTest):
def test_export(self):
ie = XmlItemExporter(self.output)
ie.start_exporting()
ie.export_item(self.i)
ie.finish_exporting()
def _get_exporter(self):
return XmlItemExporter(self.output)
def _check_output(self):
expected_value = '<?xml version="1.0" encoding="iso-8859-1"?>\n<items><item><age>22</age><name>John</name></item></items>'
self.assertEqual(self.output.getvalue(), expected_value)
class JsonLinesItemExporterTest(BaseTest):
class JsonLinesItemExporterTest(BaseItemExporterTest):
def setUp(self):
def _get_exporter(self):
try:
import json
except ImportError:
@ -144,17 +138,10 @@ class JsonLinesItemExporterTest(BaseTest):
import simplejson
except ImportError:
raise unittest.SkipTest("simplejson module not available")
super(JsonLinesItemExporterTest, self).setUp()
def test_export(self):
from scrapy.contrib.exporter.jsonlines import JsonLinesItemExporter
return JsonLinesItemExporter(self.output)
ie = JsonLinesItemExporter(self.output)
ie.start_exporting()
ie.export_item(self.i)
ie.finish_exporting()
def _check_output(self):
self.assertEqual(self.output.getvalue(), '{"age": "22", "name": "John"}\n')