# Licensed under a 3-clause BSD style license - see LICENSE.rst import re import numpy as np import pytest from astropy.time import Time, conf, TimeYearDayTime iso_times = ['2000-02-29', '1981-12-31 12:13', '1981-12-31 12:13:14', '2020-12-31 12:13:14.56'] isot_times = [re.sub(' ', 'T', tm) for tm in iso_times] yday_times = ['2000:060', '1981:365:12:13:14', '1981:365:12:13', '2020:366:12:13:14.56'] # Transpose the array to check that strides are dealt with correctly. yday_array = np.array([['2000:060', '1981:365:12:13:14'], ['1981:365:12:13', '2020:366:12:13:14.56']]).T def test_fast_conf(): # Default is to try C parser and then Python parser. Both fail so we get the # Python message. assert conf.use_fast_parser == 'True' # default with pytest.raises(ValueError, match='Time 2000:0601 does not match yday format'): Time('2000:0601', format='yday') # This is one case where Python parser is different from C parser because the # Python parser has a bug and fails with a trailing ".", but C parser works. Time('2020:150:12:13:14.', format='yday') with conf.set_temp('use_fast_parser', 'force'): Time('2020:150:12:13:14.', format='yday') with conf.set_temp('use_fast_parser', 'False'): with pytest.raises(ValueError, match='could not convert string to float'): Time('2020:150:12:13:14.', format='yday') with conf.set_temp('use_fast_parser', 'False'): assert conf.use_fast_parser == 'False' # Make sure that this is really giving the Python parser with pytest.raises(ValueError, match='Time 2000:0601 does not match yday format'): Time('2000:0601', format='yday') with conf.set_temp('use_fast_parser', 'force'): assert conf.use_fast_parser == 'force' # Make sure that this is really giving the Python parser err = 'fast C time string parser failed: time string ends in middle of component' with pytest.raises(ValueError, match=err): Time('2000:0601', format='yday') @pytest.mark.parametrize('times,format', [(iso_times, 'iso'), (isot_times, 'isot'), (yday_times, 'yday'), (yday_array, 'yday')]) @pytest.mark.parametrize('variant', [0, 1, 2]) def test_fast_matches_python(times, format, variant): if variant == 0: pass # list/array of different values (null terminated strings included) elif variant == 1: times = times[-1] # scalar elif variant == 2: times = [times[-1]] * 2 # list/array of identical values (no null terminations) with conf.set_temp('use_fast_parser', 'False'): tms_py = Time(times, format=format) with conf.set_temp('use_fast_parser', 'force'): tms_c = Time(times, format=format) # Times are binary identical assert np.all(tms_py == tms_c) def test_fast_yday_exceptions(): # msgs = {1: 'time string ends at beginning of component where break is not allowed', # 2: 'time string ends in middle of component', # 3: 'required delimiter character not found', # 4: 'non-digit found where digit (0-9) required', # 5: 'bad day of year (1 <= doy <= 365 or 366 for leap year'} with conf.set_temp('use_fast_parser', 'force'): for times, err in [('2020:150:12', 'time string ends at beginning of component'), ('2020:150:1', 'time string ends in middle of component'), ('2020:150*12:13:14', 'required delimiter character'), ('2020:15*:12:13:14', 'non-digit found where digit'), ('2020:999:12:13:14', 'bad day of year')]: with pytest.raises(ValueError, match=err): Time(times, format='yday') def test_fast_iso_exceptions(): with conf.set_temp('use_fast_parser', 'force'): for times, err in [('2020-10-10 12', 'time string ends at beginning of component'), ('2020-10-10 1', 'time string ends in middle of component'), ('2020*10-10 12:13:14', 'required delimiter character'), ('2020-10-10 *2:13:14', 'non-digit found where digit')]: with pytest.raises(ValueError, match=err): Time(times, format='iso') def test_fast_non_ascii(): with pytest.raises(ValueError, match='input is not pure ASCII'): with conf.set_temp('use_fast_parser', 'force'): Time('2020-01-01 1ᛦ:13:14.4324') def test_fast_subclass(): """Test subclass where use_fast_parser class attribute is not in __dict__""" class TimeYearDayTimeSubClass(TimeYearDayTime): name = 'yday_subclass' # Inheritance works assert hasattr(TimeYearDayTimeSubClass, 'fast_parser_pars') assert 'fast_parser_pars' not in TimeYearDayTimeSubClass.__dict__ try: # For YearDayTime, forcing the fast parser with a bad date will give # "fast C time string parser failed: time string ends in middle of component". # But since YearDayTimeSubClass does not have fast_parser_pars it will # use the Python parser. with pytest.raises(ValueError, match='Time 2000:0601 does not match yday_subclass format'): with conf.set_temp('use_fast_parser', 'force'): Time('2000:0601', format='yday_subclass') finally: del TimeYearDayTimeSubClass._registry['yday_subclass']