diff --git a/scrapy/contrib_exp/newitem/fields.py b/scrapy/contrib_exp/newitem/fields.py index 63bd6e777..44dbef5e6 100644 --- a/scrapy/contrib_exp/newitem/fields.py +++ b/scrapy/contrib_exp/newitem/fields.py @@ -41,73 +41,76 @@ class DateField(BaseField): ansi_date_re = re.compile(r'^\d{4}-\d{1,2}-\d{1,2}$') def to_python(self, value): - if value is None: - return value if isinstance(value, datetime.datetime): return value.date() - if isinstance(value, datetime.date): + elif isinstance(value, datetime.date): return value + elif isinstance(value, basestring): + if not self.ansi_date_re.search(value): + raise ValueError("Enter a valid date in YYYY-MM-DD format.") - if not self.ansi_date_re.search(value): - raise ValueError("Enter a valid date in YYYY-MM-DD format.") - - year, month, day = map(int, value.split('-')) - try: - return datetime.date(year, month, day) - except ValueError, e: - raise ValueError("Invalid date: %s" % str(e)) + year, month, day = map(int, value.split('-')) + try: + return datetime.date(year, month, day) + except ValueError, e: + raise ValueError("Invalid date: %s" % str(e)) + else: + raise TypeError("Cannot instatiante %s with %s" \ + % (self.__class__.__name__, type(value).__name__)) class DateTimeField(BaseField): def to_python(self, value): - if value is None: - return value if isinstance(value, datetime.datetime): return value - if isinstance(value, datetime.date): + elif isinstance(value, datetime.date): return datetime.datetime(value.year, value.month, value.day) - - # Attempt to parse a datetime: - value = str(value) - # split usecs, because they are not recognized by strptime. - if '.' in value: - try: - value, usecs = value.split('.') - usecs = int(usecs) - except ValueError: - raise ValueError('Enter a valid date/time in YYYY-MM-DD HH:MM[:ss[.uuuuuu]] format.') - else: - usecs = 0 - kwargs = {'microsecond': usecs} - try: # Seconds are optional, so try converting seconds first. - return datetime.datetime(*time.strptime(value, '%Y-%m-%d %H:%M:%S')[:6], - **kwargs) - - except ValueError: - try: # Try without seconds. - return datetime.datetime(*time.strptime(value, '%Y-%m-%d %H:%M')[:5], - **kwargs) - except ValueError: # Try without hour/minutes/seconds. + elif isinstance(value, basestring): + # Attempt to parse a datetime: + value = str(value) + # split usecs, because they are not recognized by strptime. + if '.' in value: try: - return datetime.datetime(*time.strptime(value, '%Y-%m-%d')[:3], - **kwargs) + value, usecs = value.split('.') + usecs = int(usecs) except ValueError: raise ValueError('Enter a valid date/time in YYYY-MM-DD HH:MM[:ss[.uuuuuu]] format.') + else: + usecs = 0 + kwargs = {'microsecond': usecs} + try: # Seconds are optional, so try converting seconds first. + return datetime.datetime(*time.strptime(value, '%Y-%m-%d %H:%M:%S')[:6], + **kwargs) + + except ValueError: + try: # Try without seconds. + return datetime.datetime(*time.strptime(value, '%Y-%m-%d %H:%M')[:5], + **kwargs) + except ValueError: # Try without hour/minutes/seconds. + try: + return datetime.datetime(*time.strptime(value, '%Y-%m-%d')[:3], + **kwargs) + except ValueError: + raise ValueError('Enter a valid date/time in YYYY-MM-DD HH:MM[:ss[.uuuuuu]] format.') + else: + raise TypeError("Cannot instatiante %s with %s" \ + % (self.__class__.__name__, type(value).__name__)) + class DecimalField(BaseField): def to_python(self, value): - return decimal.Decimal(value) if value is not None else None + return decimal.Decimal(value) class FloatField(BaseField): def to_python(self, value): - return float(value) if value is not None else None + return float(value) class IntegerField(BaseField): def to_python(self, value): - return int(value) if value is not None else None + return int(value) class TextField(BaseField): @@ -116,46 +119,44 @@ class TextField(BaseField): return self.to_python(self.to_single(value)) elif isinstance(value, unicode): return value - elif value is None: - return value else: - raise ValueError("TextField expects a unicode, got %s" \ - % type(value).__name__) + raise TypeError("%s requires a unicode (or iterable of unicodes), got %s" \ + % (self.__class__.__name__, type(value).__name__)) def to_single(self, value): - "Converts the input iterable into a single value." - return ' '.join(value) + """Converts the input iterable into a single value""" + return ' '.join((self.to_python(x) for x in value)) class TimeField(BaseField): def to_python(self, value): - if value is None: - return None if isinstance(value, datetime.time): return value if isinstance(value, datetime.datetime): return value.time + elif isinstance(value, basestring): + # Attempt to parse a datetime: + value = str(value) + # split usecs, because they are not recognized by strptime. + if '.' in value: + try: + value, usecs = value.split('.') + usecs = int(usecs) + except ValueError: + raise ValueError('Enter a valid time in HH:MM[:ss[.uuuuuu]] format.') + else: + usecs = 0 + kwargs = {'microsecond': usecs} - # Attempt to parse a datetime: - value = str(value) - # split usecs, because they are not recognized by strptime. - if '.' in value: - try: - value, usecs = value.split('.') - usecs = int(usecs) + try: # Seconds are optional, so try converting seconds first. + return datetime.time(*time.strptime(value, '%H:%M:%S')[3:6], + **kwargs) except ValueError: - raise ValueError('Enter a valid time in HH:MM[:ss[.uuuuuu]] format.') + try: # Try without seconds. + return datetime.time(*time.strptime(value, '%H:%M')[3:5], + **kwargs) + except ValueError: + raise ValueError('Enter a valid time in HH:MM[:ss[.uuuuuu]] format.') else: - usecs = 0 - kwargs = {'microsecond': usecs} - - try: # Seconds are optional, so try converting seconds first. - return datetime.time(*time.strptime(value, '%H:%M:%S')[3:6], - **kwargs) - except ValueError: - try: # Try without seconds. - return datetime.time(*time.strptime(value, '%H:%M')[3:5], - **kwargs) - except ValueError: - raise ValueError('Enter a valid time in HH:MM[:ss[.uuuuuu]] format.') - + raise TypeError("Cannot instatiante %s with %s" \ + % (self.__class__.__name__, type(value).__name__)) diff --git a/scrapy/tests/test_newitem.py b/scrapy/tests/test_newitem.py index a7dea5fee..d1fbc8dc5 100644 --- a/scrapy/tests/test_newitem.py +++ b/scrapy/tests/test_newitem.py @@ -68,7 +68,7 @@ class NewItemTest(unittest.TestCase): class TestItem(Item): name = fields.TextField(default=3) - self.assertRaises(ValueError, set_wrong_default) + self.assertRaises(TypeError, set_wrong_default) def test_to_python_iter(self): class TestItem(Item): @@ -134,16 +134,19 @@ class NewItemFieldsTest(unittest.TestCase): i = TestItem() i.field = True - assert i.field == True + assert i.field is True i.field = 1 - assert i.field == True + assert i.field is True i.field = False - assert i.field == False + assert i.field is False i.field = 0 - assert i.field == False + assert i.field is False + + i.field = None + assert i.field is False def test_date_field(self): class TestItem(Item): @@ -172,6 +175,8 @@ class NewItemFieldsTest(unittest.TestCase): self.assertRaises(ValueError, set_invalid_date) + self.assertRaises(TypeError, setattr, i, 'field', None) + def test_datetime_field(self): class TestItem(Item): field = fields.DateTimeField() @@ -214,6 +219,8 @@ class NewItemFieldsTest(unittest.TestCase): self.assertRaises(ValueError, set_invalid_date) + self.assertRaises(TypeError, setattr, i, 'field', None) + def test_decimal_field(self): class TestItem(Item): field = fields.DecimalField() @@ -231,6 +238,8 @@ class NewItemFieldsTest(unittest.TestCase): self.assertRaises(decimal.InvalidOperation, set_invalid_value) + self.assertRaises(TypeError, setattr, i, 'field', None) + def test_float_field(self): class TestItem(Item): field = fields.FloatField() @@ -248,6 +257,8 @@ class NewItemFieldsTest(unittest.TestCase): self.assertRaises(ValueError, set_invalid_value) + self.assertRaises(TypeError, setattr, i, 'field', None) + def test_integer_field(self): class TestItem(Item): field = fields.IntegerField() @@ -265,6 +276,8 @@ class NewItemFieldsTest(unittest.TestCase): self.assertRaises(ValueError, set_invalid_value) + self.assertRaises(TypeError, setattr, i, 'field', None) + def test_text_field(self): class TestItem(Item): field = fields.TextField() @@ -278,12 +291,23 @@ class NewItemFieldsTest(unittest.TestCase): def set_str(): i.field = 'string' - self.assertRaises(ValueError, set_str) + # must be unicode! + self.assertRaises(TypeError, set_str) + + self.assertRaises(TypeError, setattr, i, 'field', None) def set_invalid_value(): i.field = 3 - self.assertRaises(ValueError, set_invalid_value) + self.assertRaises(TypeError, set_invalid_value) + + i = TestItem() + i.field = [u'hello', u'world'] + self.assertEqual(i.field, u'hello world') + assert isinstance(i.field, unicode) + + self.assertRaises(TypeError, setattr, i, 'field', [u'hello', 3, u'world']) + self.assertRaises(TypeError, setattr, i, 'field', [u'hello', 'world']) def test_time_field(self): class TestItem(Item): @@ -295,6 +319,8 @@ class NewItemFieldsTest(unittest.TestCase): i.field = dt_t assert i.field == dt_t + self.assertRaises(TypeError, setattr, i, 'field', None) + dt_dt = datetime.datetime.today() i.field = dt_dt assert i.field == dt_dt.time