From 38b1c0b1bed6f0cd3356ec2dd200e7b46ca3decd Mon Sep 17 00:00:00 2001 From: Elias Dorneles Date: Fri, 24 Jul 2015 11:53:11 -0300 Subject: [PATCH] fixes to work under Python3 and PyPy --- .travis.yml | 3 +++ dateparser/__init__.py | 2 +- dateparser/date.py | 14 ++++------- dateparser/date_parser.py | 38 ++++++++--------------------- dateparser/freshness_date_parser.py | 2 +- dateparser/languages/dictionary.py | 17 +++++++------ dateparser/languages/language.py | 12 ++++----- dateparser/languages/loader.py | 7 +++--- dateparser/languages/validation.py | 25 ++++++++++--------- tests/__init__.py | 6 +++-- tests/test_date.py | 16 ++++++------ tests/test_date_parser.py | 22 +++++++++-------- tests/test_freshness_date_parser.py | 5 ++-- tests/test_timezone_parser.py | 1 - tox.ini | 2 +- 15 files changed, 80 insertions(+), 92 deletions(-) diff --git a/.travis.yml b/.travis.yml index 067c29297..48254fb42 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,9 @@ language: python python: - "2.7" + - "3.3" + - "3.4" + - "pypy" env: - TOXENV=py27 install: diff --git a/dateparser/__init__.py b/dateparser/__init__.py index 216950b7f..ec324baf6 100644 --- a/dateparser/__init__.py +++ b/dateparser/__init__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- __version__ = '0.2.1' -from date import DateDataParser +from .date import DateDataParser _default_parser = DateDataParser(allow_redetect_language=True) diff --git a/dateparser/date.py b/dateparser/date.py index 0bdaf399c..34cb38117 100644 --- a/dateparser/date.py +++ b/dateparser/date.py @@ -2,8 +2,8 @@ import calendar import collections import re +import six from datetime import datetime, timedelta -from types import NoneType from warnings import warn from dateutil.relativedelta import relativedelta @@ -99,10 +99,6 @@ def parse_with_formats(date_string, date_formats): :returns: :class:`datetime.datetime`, dict or None """ - # Encode to support locale setting in spiders - if isinstance(date_string, unicode): - date_string = date_string.encode('utf-8') - period = 'day' for date_format in date_formats: try: @@ -130,10 +126,10 @@ class _DateLanguageParser(object): DATE_FORMATS_ERROR_MESSAGE = "Date formats should be list, tuple or set of strings" def __init__(self, language, date_string, date_formats): - if isinstance(date_formats, basestring): + if isinstance(date_formats, six.string_types): warn(self.DATE_FORMATS_ERROR_MESSAGE, FutureWarning) date_formats = [date_formats] - elif not isinstance(date_formats, (list, tuple, collections.Set, NoneType)): + elif not (date_formats is None or isinstance(date_formats, (list, tuple, collections.Set))): raise TypeError(self.DATE_FORMATS_ERROR_MESSAGE) self.language = language @@ -261,13 +257,13 @@ def __init__(self, languages=None, allow_redetect_language=False): if allow_redetect_language: self.language_detector = AutoDetectLanguage( - languages if languages else available_language_map.values(), + languages if languages else list(available_language_map.values()), allow_redetection=True) elif languages: self.language_detector = ExactLanguages(languages=languages) else: self.language_detector = AutoDetectLanguage( - available_language_map.values(), allow_redetection=False) + list(available_language_map.values()), allow_redetection=False) def get_date_data(self, date_string, date_formats=None): """ diff --git a/dateparser/date_parser.py b/dateparser/date_parser.py index e7d994a19..fee120503 100644 --- a/dateparser/date_parser.py +++ b/dateparser/date_parser.py @@ -2,42 +2,22 @@ from __future__ import unicode_literals import calendar -import re, sys +import re +import sys from datetime import datetime from collections import OrderedDict +import six from dateutil import parser from dateutil.relativedelta import relativedelta from dateparser.timezone_parser import pop_tz_offset_from_string, convert_to_local_tz -from conf import settings +from .conf import settings binary_type = bytes if sys.version_info[0] == 3 else str -class new_relativedelta(relativedelta): - """ dateutil does not check if result of parsing weekday is in the future. - Although items dates are already in the past, so we need to fix this particular case. - """ - - def __new__(cls, *args, **kwargs): - if not args and len(kwargs) == 1 and 'weekday' in kwargs: - return super(new_relativedelta, cls).__new__(cls, *args, **kwargs) - else: - # use original class to parse other cases - return relativedelta(*args, **kwargs) - - def __add__(self, other): - ret = super(new_relativedelta, self).__add__(other) - if ret > datetime.utcnow(): - ret -= relativedelta(days=7) - return ret - - -parser.relativedelta.relativedelta = new_relativedelta - - class new_parser(parser.parser): """ Implements an alternate parse method which supports preference to dates in future and past. @@ -72,7 +52,7 @@ def get_period(res): ('month', ['month']), ('year', ['year']), ]) - for period, markers in periods.iteritems(): + for period, markers in six.iteritems(periods): for marker in markers: if getattr(res, marker) is not None: return period @@ -95,7 +75,9 @@ def _populate(cls, res, default): # Fix weekday if res.weekday is not None and not res.day: - new_date = new_date + new_relativedelta(weekday=res.weekday) + new_date = new_date + relativedelta(weekday=res.weekday) + if new_date > datetime.utcnow(): + new_date -= relativedelta(days=7) # Correct date and return return cls._correct(new_date, [key + 's' for key in repl.keys()], default) @@ -177,14 +159,14 @@ def dateutil_parse(date_string, **kwargs): # https://bugs.launchpad.net/dateutil/+bug/1042851 try: return new_parser().parse(date_string, **kwargs) - except TypeError, e: + except TypeError as e: raise ValueError(e, "Invalid date: %s" % date_string) class DateParser(object): def parse(self, date_string): - date_string = unicode(date_string) + date_string = six.text_type(date_string) if not date_string.strip(): raise ValueError("Empty string") diff --git a/dateparser/freshness_date_parser.py b/dateparser/freshness_date_parser.py index f3dcf4386..72290cc35 100644 --- a/dateparser/freshness_date_parser.py +++ b/dateparser/freshness_date_parser.py @@ -26,7 +26,7 @@ def _are_all_words_units(self, date_string): words = filter(lambda x: x if x else False, re.split('\W', date_string)) words = filter(lambda x: not re.match(r'%s' % '|'.join(skip), x), words) - return not bool(words) + return not list(words) def _parse_time(self, date_string): """Attemps to parse time part of date strings like '1 day ago, 2 PM' """ diff --git a/dateparser/languages/dictionary.py b/dateparser/languages/dictionary.py index f791aa6da..760f10f32 100644 --- a/dateparser/languages/dictionary.py +++ b/dateparser/languages/dictionary.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- +from __future__ import unicode_literals import re -from itertools import izip_longest +from six.moves import zip_longest from operator import methodcaller DATEUTIL_PARSER_HARDCODED_TOKENS = [":", ".", " ", "-", "/"] # Consts used in dateutil.parser._parse @@ -21,19 +22,19 @@ def __init__(self, language_info): if 'skip' in language_info: skip = map(methodcaller('lower'), language_info['skip']) - dictionary.update(izip_longest(skip, [], fillvalue=None)) + dictionary.update(zip_longest(skip, [], fillvalue=None)) if 'pertain' in language_info: pertain = map(methodcaller('lower'), language_info['pertain']) - dictionary.update(izip_longest(pertain, [], fillvalue=None)) + dictionary.update(zip_longest(pertain, [], fillvalue=None)) for word in ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday', 'january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december', 'year', 'month', 'week', 'day', 'hour', 'minute', 'second', 'ago']: translations = map(methodcaller('lower'), language_info[word]) - dictionary.update(izip_longest(translations, [], fillvalue=word)) - dictionary.update(izip_longest(ALWAYS_KEEP_TOKENS, ALWAYS_KEEP_TOKENS)) - dictionary.update(izip_longest(map(methodcaller('lower'), + dictionary.update(zip_longest(translations, [], fillvalue=word)) + dictionary.update(zip_longest(ALWAYS_KEEP_TOKENS, ALWAYS_KEEP_TOKENS)) + dictionary.update(zip_longest(map(methodcaller('lower'), DATEUTIL_PARSERINFO_KNOWN_TOKENS), DATEUTIL_PARSERINFO_KNOWN_TOKENS)) @@ -84,7 +85,7 @@ def _get_split_regex(self): def _construct_split_regex(self): known_words_group = u"|".join(map(re.escape, self._get_sorted_words())) if self._no_word_spacing: - regex = ur"^(.*?)({})(.*)$".format(known_words_group) + regex = r"^(.*?)({})(.*)$".format(known_words_group) else: - regex = ur"^(.*?(?:\A|\d|_|\W))({})((?:\d|_|\W|\Z).*)$".format(known_words_group) + regex = r"^(.*?(?:\A|\d|_|\W))({})((?:\d|_|\W|\Z).*)$".format(known_words_group) self._split_regex = re.compile(regex, re.UNICODE | re.IGNORECASE) diff --git a/dateparser/languages/language.py b/dateparser/languages/language.py index 3fc04f04f..9508b1673 100644 --- a/dateparser/languages/language.py +++ b/dateparser/languages/language.py @@ -20,7 +20,7 @@ def __init__(self, shortname, language_info): self.shortname = shortname self.info = language_info.copy() for simplification in self.info.get('simplifications', []): - key, value = simplification.items()[0] + key, value = list(simplification.items())[0] if isinstance(value, int): simplification[key] = str(value) @@ -51,15 +51,15 @@ def translate(self, date_string, keep_formatting=False): if word in dictionary: words[i] = dictionary[word] or '' - return self._join(filter(bool, words), separator="" if keep_formatting else " ") + return self._join(list(filter(bool, words)), separator="" if keep_formatting else " ") def _simplify(self, date_string): date_string = date_string.lower() for simplification in self.info.get('simplifications', []): - pattern, replacement = simplification.items()[0] + pattern, replacement = list(simplification.items())[0] if not self.info.get('no_word_spacing', False): replacement = wrap_replacement_for_regex(replacement, pattern) - pattern = ur'(\A|\d|_|\W)%s(\d|_|\W|\Z)' % pattern + pattern = r'(\A|\d|_|\W)%s(\d|_|\W|\Z)' % pattern date_string = re.sub(pattern, replacement, date_string, flags=re.IGNORECASE | re.UNICODE).lower() return date_string @@ -83,8 +83,8 @@ def _are_all_words_in_the_dictionary(self, words): def _split(self, date_string, keep_formatting): tokens = [date_string] - tokens = self._split_tokens_with_regex(tokens, "(\d+)") - tokens = self._split_tokens_by_known_words(tokens, keep_formatting) + tokens = list(self._split_tokens_with_regex(tokens, "(\d+)")) + tokens = list(self._split_tokens_by_known_words(tokens, keep_formatting)) return tokens def _split_tokens_with_regex(self, tokens, regex): diff --git a/dateparser/languages/loader.py b/dateparser/languages/loader.py index 262f9102b..d4090c761 100644 --- a/dateparser/languages/loader.py +++ b/dateparser/languages/loader.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from pkgutil import get_data +import six from yaml import load as load_yaml from .language import Language @@ -11,7 +12,7 @@ class LanguageDataLoader(object): _data = None def __init__(self, file=None): - if isinstance(file, basestring): + if isinstance(file, six.string_types): file = open(file) self.file = file @@ -39,7 +40,7 @@ def _load_data(self): base_data = data.pop('base', {'skip': []}) base_data['skip'] += settings.SKIP_TOKENS known_languages = {} - for shortname, language_info in data.iteritems(): + for shortname, language_info in six.iteritems(data): self._update_language_info_with_base_info(language_info, base_data) language = Language(shortname, language_info) if language.validate_info(): @@ -47,7 +48,7 @@ def _load_data(self): self._data = known_languages def _update_language_info_with_base_info(self, language_info, base_info): - for key, values in base_info.iteritems(): + for key, values in six.iteritems(base_info): if isinstance(values, list): extended_values = (values + language_info[key]) if key in language_info else values language_info[key] = extended_values diff --git a/dateparser/languages/validation.py b/dateparser/languages/validation.py index d3b4fd7b6..9893f3f16 100644 --- a/dateparser/languages/validation.py +++ b/dateparser/languages/validation.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- import re +import six from dateparser.utils import get_logger @@ -54,7 +55,7 @@ def _validate_type(cls, language_id, info): def _validate_name(cls, language_id, info): result = True - if 'name' not in info or not isinstance(info['name'], basestring) or not info['name']: + if 'name' not in info or not isinstance(info['name'], six.string_types) or not info['name']: cls.get_logger().error("Language '%(id)s' does not have a name", {'id': language_id}) result = False @@ -86,7 +87,7 @@ def _validate_skip_list(cls, language_id, info): skip_tokens_list = info['skip'] if isinstance(skip_tokens_list, list): for token in skip_tokens_list: - if not isinstance(token, basestring) or not token: + if not isinstance(token, six.string_types) or not token: cls.get_logger().error( "Invalid 'skip' token %(token)r for '%(id)s' language: " "expected not empty string", @@ -111,7 +112,7 @@ def _validate_pertain_list(cls, language_id, info): pertain_tokens_list = info['skip'] if isinstance(pertain_tokens_list, list): for token in pertain_tokens_list: - if not isinstance(token, basestring) or not token: + if not isinstance(token, six.string_types) or not token: cls.get_logger().error( "Invalid 'pertain' token %(token)r for '%(id)s' language: " "expected not empty string", @@ -141,7 +142,7 @@ def _validate_weekdays(cls, language_id, info): translations_list = info[weekday] if isinstance(translations_list, list): for token in translations_list: - if not isinstance(token, basestring) or not token: + if not isinstance(token, six.string_types) or not token: cls.get_logger().error( "Invalid '%(weekday)s' translation %(token)r for '%(id)s' language: " "expected not empty string", @@ -174,7 +175,7 @@ def _validate_months(cls, language_id, info): translations_list = info[month] if isinstance(translations_list, list): for token in translations_list: - if not isinstance(token, basestring) or not token: + if not isinstance(token, six.string_types) or not token: cls.get_logger().error( "Invalid '%(month)s' translation %(token)r for '%(id)s' language: " "expected not empty string", @@ -204,7 +205,7 @@ def _validate_units(cls, language_id, info): translations_list = info[unit] if isinstance(translations_list, list): for token in translations_list: - if not isinstance(token, basestring) or not token: + if not isinstance(token, six.string_types) or not token: cls.get_logger().error( "Invalid '%(unit)s' translation %(token)r for '%(id)s' language: " "expected not empty string", @@ -234,7 +235,7 @@ def _validate_other_words(cls, language_id, info): translations_list = info[word] if isinstance(translations_list, list): for token in translations_list: - if not isinstance(token, basestring) or not token: + if not isinstance(token, six.string_types) or not token: cls.get_logger().error( "Invalid '%(word)s' translation %(token)r for '%(id)s' language: " "expected not empty string", @@ -267,8 +268,8 @@ def _validate_simplifications(cls, language_id, info): result = False continue - key, value = simplification.items()[0] - if not isinstance(key, basestring) or not isinstance(value, (basestring, int)): + key, value = list(simplification.items())[0] + if not isinstance(key, six.string_types) or not isinstance(value, (six.string_types, int)): cls.get_logger().error( "Invalid simplification %(simplification)r for '%(id)s' language: " "each simplification suppose to be string-to-string-or-int mapping", @@ -277,7 +278,7 @@ def _validate_simplifications(cls, language_id, info): continue compiled_key = re.compile(key) - value = unicode(value) + value = six.text_type(value) replacements = re.findall(r'\\(\d+)', value) replacements.extend(re.findall(r'\\g<(.+?)>', value)) @@ -308,7 +309,7 @@ def _validate_simplifications(cls, language_id, info): "unknown groups %(groups)s", {'simplification': simplification, 'id': language_id, - 'groups': ", ".join(map(unicode, sorted(extra_groups)))}) + 'groups': ", ".join(map(six.text_type, sorted(extra_groups)))}) result = False if not_used_groups: @@ -317,7 +318,7 @@ def _validate_simplifications(cls, language_id, info): "groups %(groups)s were not used", {'simplification': simplification, 'id': language_id, - 'groups': ", ".join(map(unicode, sorted(not_used_groups)))}) + 'groups': ", ".join(map(six.text_type, sorted(not_used_groups)))}) result = False else: cls.get_logger().error( diff --git a/tests/__init__.py b/tests/__init__.py index 2f0ffc049..67db79110 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -18,6 +18,8 @@ def tearDown(self): for patch in reversed(self.__patches): patch.stop() - def then_error_was_raised(self, error_cls, error_message): + def then_error_was_raised(self, error_cls, allowed_substrings=()): self.assertIsInstance(self.error, error_cls) - self.assertEqual(error_message, str(self.error)) + self.assertTrue(any(mesg in str(self.error) for mesg in allowed_substrings), + "Didn't found any of the expected messages (%r) -- message was: %r" % ( + allowed_substrings, self.error)) diff --git a/tests/test_date.py b/tests/test_date.py index 0c45266e1..7e95e6822 100644 --- a/tests/test_date.py +++ b/tests/test_date.py @@ -11,11 +11,11 @@ from nose_parameterized import parameterized, param import dateparser +import six from dateparser import date from dateparser.date import get_last_day_of_month from dateparser.languages.loader import LanguageDataLoader from dateparser.languages.loader import default_language_loader -from dateparser.conf import settings from tests import BaseTestCase @@ -87,11 +87,11 @@ def then_all_dates_in_range_are_present(self, begin, end): date_under_test += timedelta(days=1) def then_range_is_in_ascending_order(self): - for i in xrange(len(self.result) - 1): + for i in six.moves.range(len(self.result) - 1): self.assertLess(self.result[i], self.result[i + 1]) def then_period_was_rejected(self, period): - self.then_error_was_raised(ValueError, 'Invalid argument: {}'.format(period)) + self.then_error_was_raised(ValueError, ['Invalid argument: {}'.format(period)]) class TestGetIntersectingPeriodsFunction(BaseTestCase): @@ -181,7 +181,7 @@ def test_should_reject_easily_mistaken_dateutil_arguments(self, period_size): self.when_intersecting_period_calculated(low=datetime(2014, 6, 15), high=datetime(2014, 6, 25), period_size=period_size) - self.then_error_was_raised(ValueError, 'Invalid period: ' + str(period_size)) + self.then_error_was_raised(ValueError, ['Invalid period: ' + str(period_size)]) @parameterized.expand([ param(low=datetime(2014, 4, 15), high=datetime(2014, 4, 14), period_size='month'), @@ -338,7 +338,7 @@ def test_should_not_assume_language_too_early(self): allow_redetect_language=False) self.when_multiple_dates_are_parsed(dates_to_parse.keys()) self.then_all_results_were_parsed() - self.then_parsed_dates_are(dates_to_parse.values()) + self.then_parsed_dates_are(list(dates_to_parse.values())) def test_should_enable_redetection_for_multiple_languages(self): dates_to_parse = OrderedDict([(u'13 Ago, 2014', datetime(2014, 8, 13).date()), # es, it, pt @@ -350,7 +350,7 @@ def test_should_enable_redetection_for_multiple_languages(self): self.given_parser(restrict_to_languages=['es', 'it', 'pt'], allow_redetect_language=True) self.when_multiple_dates_are_parsed(dates_to_parse.keys()) self.then_all_results_were_parsed() - self.then_parsed_dates_are(dates_to_parse.values()) + self.then_parsed_dates_are(list(dates_to_parse.values())) @parameterized.expand([ param("2014-10-09T17:57:39+00:00"), @@ -502,8 +502,8 @@ def then_languages_are_unknown(self, unknown_languages): self.assertIsInstance(self.error, ValueError) match = self.UNKNOWN_LANGUAGES_EXCEPTION_RE.match(str(self.error)) self.assertTrue(match) - languages = [shortname[2:-1] for shortname in match.group(1).split(", ")] - self.assertItemsEqual(languages, unknown_languages) + languages = match.group(1).split(", ") + six.assertCountEqual(self, languages, [repr(l) for l in unknown_languages]) if __name__ == '__main__': diff --git a/tests/test_date_parser.py b/tests/test_date_parser.py index 000c5448f..b02048399 100644 --- a/tests/test_date_parser.py +++ b/tests/test_date_parser.py @@ -10,6 +10,7 @@ from nose_parameterized import parameterized, param import dateparser.timezone_parser +import six from dateparser.date import DateDataParser, date_parser from dateparser.date_parser import DateParser from dateparser.languages import default_language_loader @@ -114,23 +115,23 @@ def given_parser_languages_are(self, languages): for language in languages] def when_all_languages_are_detected(self, date_strings, modify=False): - assert not isinstance(date_strings, basestring) + assert not isinstance(date_strings, six.string_types) for date_string in date_strings: detected_languages = list(self.parser.iterate_applicable_languages(date_string, modify=modify)) self.detected_languages = detected_languages def when_one_language_is_detected(self, date_strings, modify=False): for date_string in date_strings: - detected_language = self.parser.iterate_applicable_languages(date_string, modify=modify).next() + detected_language = next(self.parser.iterate_applicable_languages(date_string, modify=modify)) self.detected_languages = [detected_language] def then_detected_languages_are(self, expected_languages): shortnames = map(attrgetter('shortname'), self.detected_languages) - self.assertItemsEqual(expected_languages, shortnames) + six.assertCountEqual(self, expected_languages, shortnames) def then_parser_languages_are(self, expected_languages): shortnames = map(attrgetter('shortname'), self.parser.languages) - self.assertItemsEqual(expected_languages, shortnames) + six.assertCountEqual(self, expected_languages, shortnames) class ExactLanguagesTest(BaseTestCase): @@ -141,7 +142,7 @@ def setUp(self): def test_languages_passed_in_constructor_should_not_be_none(self): self.when_parser_is_constructed(languages=None) - self.then_error_was_raised(ValueError, 'language cannot be None for ExactLanguages') + self.then_error_was_raised(ValueError, ['language cannot be None for ExactLanguages']) @parameterized.expand([ param(languages=['es'], date_strings=["13 Ago, 2014"]), @@ -169,7 +170,7 @@ def given_parser(self, languages): self.parser = ExactLanguages(languages) def when_languages_are_detected(self, date_strings, modify=False): - assert not isinstance(date_strings, basestring) + assert not isinstance(date_strings, six.string_types) for date_string in date_strings: detected_languages = list(self.parser.iterate_applicable_languages(date_string, modify=modify)) self.detected_languages = detected_languages @@ -182,7 +183,7 @@ def when_parser_is_constructed(self, languages): def then_detected_languages_are(self, expected_languages): shortnames = map(attrgetter('shortname'), self.detected_languages) - self.assertItemsEqual(expected_languages, shortnames) + six.assertCountEqual(self, expected_languages, shortnames) class TestDateParser(BaseTestCase): @@ -320,7 +321,7 @@ def test_parsing_with_utc_offsets(self, date_string, expected): def test_empty_dates_string_is_not_parsed(self): self.when_date_is_parsed_by_date_parser('') - self.then_error_was_raised(ValueError, "Empty string") + self.then_error_was_raised(ValueError, ["Empty string"]) @parameterized.expand([ param('invalid date string'), @@ -329,7 +330,7 @@ def test_empty_dates_string_is_not_parsed(self): ]) def test_dates_not_parsed(self, date_string): self.when_date_is_parsed_by_date_parser(date_string) - self.then_error_was_raised(ValueError, "unknown string format") + self.then_error_was_raised(ValueError, ["unknown string format"]) @parameterized.expand([ param('10 December', datetime(2014, 12, 10)), @@ -455,7 +456,7 @@ def test_that_day_preference_does_not_affect_dates_with_explicit_day(self, prefe ]) def test_error_should_be_raised_for_invalid_dates_with_too_large_day_number(self, date_string): self.when_date_is_parsed_by_date_parser(date_string) - self.then_error_was_raised(ValueError, 'Day not in range for month') + self.then_error_was_raised(ValueError, ['Day not in range for month']) @parameterized.expand([ param('2015-05-02T10:20:19+0000', languages=['fr'], expected=datetime(2015, 5, 2, 10, 20, 19)), @@ -537,6 +538,7 @@ def then_date_obj_exactly_is(self, expected): self.assertEqual(expected, self.result['date_obj']) def then_date_was_parsed_by_date_parser(self): + self.assertNotEqual(NotImplemented, self.date_result, "Date was not parsed") self.assertEqual(self.result['date_obj'], self.date_result[0]) diff --git a/tests/test_freshness_date_parser.py b/tests/test_freshness_date_parser.py index 744537d00..6e3361bb9 100644 --- a/tests/test_freshness_date_parser.py +++ b/tests/test_freshness_date_parser.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -import re +import six import unittest from datetime import datetime, timedelta, date, time from functools import wraps @@ -251,7 +251,8 @@ def test_dates_not_supported_by_date_time(self, date_string): self.given_parser() self.given_date_string(date_string) self.when_date_is_parsed() - self.then_error_was_raised(ValueError, 'year is out of range') + self.then_error_was_raised(ValueError, ['year is out of range', + "('year must be in 1..9999'"]) @parameterized.expand([ param('несколько секунд назад', boundary={'seconds': 45}, period='day'), diff --git a/tests/test_timezone_parser.py b/tests/test_timezone_parser.py index 032ab0b03..587417bd2 100644 --- a/tests/test_timezone_parser.py +++ b/tests/test_timezone_parser.py @@ -29,7 +29,6 @@ def setUp(self): param('15 May 2004', None), ]) def test_extracting_valid_offset(self, initial_string, expected_offset): - print self.datetime_string, self.timezone_offset self.given_string(initial_string) self.when_offset_popped_from_string() self.then_offset_is(expected_offset) diff --git a/tox.ini b/tox.ini index ef6451af5..11c92c4ae 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27 +envlist = py27, py33, py34, pypy [testenv] deps =