You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

102 lines
2.6 KiB

4 years ago
  1. """
  2. Scrapy Item
  3. See documentation in docs/topics/item.rst
  4. """
  5. from pprint import pformat
  6. from collections import MutableMapping
  7. from abc import ABCMeta
  8. import six
  9. from scrapy.utils.trackref import object_ref
  10. class BaseItem(object_ref):
  11. """Base class for all scraped items."""
  12. pass
  13. class Field(dict):
  14. """Container of field metadata"""
  15. class ItemMeta(ABCMeta):
  16. def __new__(mcs, class_name, bases, attrs):
  17. classcell = attrs.pop('__classcell__', None)
  18. new_bases = tuple(base._class for base in bases if hasattr(base, '_class'))
  19. _class = super(ItemMeta, mcs).__new__(mcs, 'x_' + class_name, new_bases, attrs)
  20. fields = getattr(_class, 'fields', {})
  21. new_attrs = {}
  22. for n in dir(_class):
  23. v = getattr(_class, n)
  24. if isinstance(v, Field):
  25. fields[n] = v
  26. elif n in attrs:
  27. new_attrs[n] = attrs[n]
  28. new_attrs['fields'] = fields
  29. new_attrs['_class'] = _class
  30. if classcell is not None:
  31. new_attrs['__classcell__'] = classcell
  32. return super(ItemMeta, mcs).__new__(mcs, class_name, bases, new_attrs)
  33. class DictItem(MutableMapping, BaseItem):
  34. fields = {}
  35. def __init__(self, *args, **kwargs):
  36. self._values = {}
  37. if args or kwargs: # avoid creating dict for most common case
  38. for k, v in six.iteritems(dict(*args, **kwargs)):
  39. self[k] = v
  40. def __getitem__(self, key):
  41. return self._values[key]
  42. def __setitem__(self, key, value):
  43. if key in self.fields:
  44. self._values[key] = value
  45. else:
  46. raise KeyError("%s does not support field: %s" %
  47. (self.__class__.__name__, key))
  48. def __delitem__(self, key):
  49. del self._values[key]
  50. def __getattr__(self, name):
  51. if name in self.fields:
  52. raise AttributeError("Use item[%r] to get field value" % name)
  53. raise AttributeError(name)
  54. def __setattr__(self, name, value):
  55. if not name.startswith('_'):
  56. raise AttributeError("Use item[%r] = %r to set field value" %
  57. (name, value))
  58. super(DictItem, self).__setattr__(name, value)
  59. def __len__(self):
  60. return len(self._values)
  61. def __iter__(self):
  62. return iter(self._values)
  63. __hash__ = BaseItem.__hash__
  64. def keys(self):
  65. return self._values.keys()
  66. def __repr__(self):
  67. return pformat(dict(self))
  68. def copy(self):
  69. return self.__class__(self)
  70. @six.add_metaclass(ItemMeta)
  71. class Item(DictItem):
  72. pass