def test_line_repeat_in_definition(): # You cannot repeat keys r = InputReader() r.add_line_key(str('red')) with raises(ReaderError) as e: r.add_line_key('red') assert search(r'The keyname "\w+" has been defined twice', str(e.value))
def test_line_case_definition(): r = InputReader() a = r.add_line_key('red', case=True) assert a._case with raises(ValueError) as e: r.add_line_key('blue', case='True') assert 'case must be bool' in str(e.value)
def test_line_read_case_sensitive(): r = InputReader() r.add_line_key('blue', type=str) inp = r.read_input(['blue HeLLo']) assert inp.blue == 'hello' r.add_line_key('red', type=str, case=True) inp = r.read_input(['red HeLLo']) assert inp.red == 'HeLLo'
def test_unknown_keys_cause_failure(setup): # Don't ignore unknown keys parse_string = setup[-1] reader = InputReader(comment='//', ignoreunknown=False) reader.add_boolean_key('red') reader.add_line_key('path') with raises(ReaderError) as e: reader.read_input(parse_string) assert 'Unrecognized key' in str(e.value)
def test_ignoreunknown_actually_ignores_unknown(setup): # Ignore unknown keys parse_string = setup[-1] reader = InputReader(comment='//', ignoreunknown=True) reader.add_boolean_key('red') reader.add_line_key('path') inp = reader.read_input(parse_string) with raises(AttributeError): inp.blue
def test_comments_are_handled_correctly(setup): parse_string = setup[-1] reader = InputReader(comment='#') reader.add_boolean_key('red') reader.add_boolean_key('blue') reader.add_line_key('path') with raises(ReaderError) as e: reader.read_input(parse_string) regex = r'expected \d+ arguments, got \d+' assert search(regex, str(e.value))
def test_line_read_using_defaults(): r = InputReader() r.add_line_key('blue') inp = r.read_input(['blue bird']) assert inp.blue == 'bird' with raises(ReaderError) as e: inp = r.read_input(['blue']) assert search(r'expected .*\d+ arguments, got \d+', str(e.value)) with raises(ReaderError) as e: inp = r.read_input(['blue bird egg']) assert search(r'expected .*\d+ arguments, got \d+', str(e.value))
def test_line_missing_keyname(): r = InputReader() with raises(TypeError): r.add_line_key() with raises(TypeError): r.add_line_key(type=str) with raises(TypeError): r.add_line_key(glob={}) with raises(TypeError): r.add_line_key(keywords={}) with raises(TypeError): r.add_line_key(case=False)
def test_line_correct_call(): r = InputReader() a = r.add_line_key('red') assert a.name == 'red' assert a._type == [str] assert a._glob == {} assert a._keywords == {} assert not a._case with raises(TypeError) as e: r.add_line_key('blue', glob={'len':'*'}, keywords={'bird':{}}) assert 'Cannot define both glob and keywords' in str(e.value) with raises(ValueError) as e: r.add_line_key('pink', type=None, glob=None, keywords=None) assert 'type, glob and keywords cannot all be empty' in str(e.value)
def test_case_sensitivity_at_class_level(): ir = InputReader(case=True) assert ir._case l = ir.add_line_key('RED') assert l._case with raises(ValueError): ir2 = InputReader(case='True')
def test_line_read_using_keywords(): r = InputReader() r.add_line_key('red', type=int, keywords={'robin':{}}) inp = r.read_input(['red 5 robin=early']) assert inp.red == (5, {'robin':'early'}) inp = r.read_input(['red 5']) assert inp.red == (5, {}) with raises(ReaderError) as e: r.read_input(['red 5 robin=early light=special']) assert 'Unknown keyword' in str(e.value) r.add_line_key('blue', type=[int, int], keywords={'egg':{'type':int, 'default':1}, 'ice':{'type':(4, 5)}}) inp = r.read_input(['blue 5 7 egg=6 ice=4']) assert inp.blue == (5, 7, {'egg':6, 'ice':4}) inp = r.read_input(['blue 5 7 ice=5']) assert inp.blue == (5, 7, {'egg':1, 'ice':5}) inp = r.read_input(['blue 5 7']) assert inp.blue == (5, 7, {'egg':1}) r.add_line_key('cyan', type=None, keywords={'by':{'type':int}, 'to':{'type':int}}) inp = r.read_input(['cyan by=14 to=11']) assert inp.cyan == {'by':14, 'to':11} inp = r.read_input(['cyan by=14']) assert inp.cyan == {'by':14} inp = r.read_input(['cyan']) assert inp.cyan == {} with raises(ReaderError) as e: inp = r.read_input(['cyan by = 3']) assert 'Error reading keyword argument' in str(e.value)
def test_line_name_definition(): r = InputReader() with raises(ValueError) as e: r.add_line_key(23) assert 'keyname must be str' in str(e.value) with raises(ValueError) as e: r.add_line_key('hello goodbye') assert 'String cannot contain spaces' in str(e.value) with raises(ValueError) as e: r.add_line_key('') assert 'String cannot be of zero length' in str(e.value)
def test_line_glob_definitions(): # Test that glob checking is OK r = InputReader() a = r.add_line_key('red', glob={'len':'*'}) assert a._glob == {'len':'*', 'type':str, 'join':False} b = r.add_line_key('blue', glob={str('len'):str('?')}) assert b._glob == {'len':'?', 'type':str, 'join':False} c = r.add_line_key('green', glob={'len':'+', 'join':True}) assert c._glob == {'len':'+', 'type':str, 'join':True} e = r.add_line_key('pink', glob={'len':'?', 'type':int}) assert e._glob == {'len':'?', 'type':int, 'join':False} f = r.add_line_key('gray', glob={'len':'*', 'type':(int,"hey",str("hi"),str)}) assert f._glob == {'len':'*', 'type':(int,"hey","hi",str), 'join':False} # Test that glob checking is OK for bad input with raises(ValueError) as e: r.add_line_key('black', glob='wrong') assert 'glob must be a dict' in str(e.value) with raises(ValueError) as e: r.add_line_key('black', glob={'len':'1'}) assert search(r'"len" must be one of "\*", "\+", or "\?" in glob', str(e.value)) with raises(ValueError) as e: r.add_line_key('black', glob={'join':True}) assert '"len" required for glob' in str(e.value) with raises(ValueError) as e: r.add_line_key('black', glob={'len':'*', 'join':'True'}) assert '"join" must be a bool in glob' in str(e.value) with raises(ValueError) as e: r.add_line_key('black', glob={'len':'*', 'type':complex}) assert 'type must be one of' in str(e.value) with raises(ValueError) as e: r.add_line_key('black', glob={'len':'*', 'type':[str]}) assert 'list not allowed in type for glob or keywords' in str(e.value) with raises(TypeError) as e: r.add_line_key('black', glob={'len':'*', 'dumb':True}) assert 'Unknown key in glob' in str(e.value) with raises(ValueError) as e: r.add_line_key('black', glob={'len':'?', 'join':True}) assert r'"join=True" makes no sense for "len=?"' in str(e.value)
def test_line_reading_types(): r = InputReader() r.add_line_key('blue', type=int) inp = r.read_input(['blue 23']) assert inp.blue == 23 with raises(ReaderError) as e: inp = r.read_input(['blue bird']) assert search(r'expected \w+, got "\w+"', str(e.value)) r.add_line_key('red', type=(0, 1, 2, 3)) inp = r.read_input(['red 3']) assert inp.red == 3 with raises(ReaderError) as e: inp = r.read_input(['red 4']) assert search(r'expected one of .+, got "\w+"', str(e.value)) r.add_line_key('green', type=(float, None)) inp = r.read_input(['green 3']) assert inp.green == 3 inp = r.read_input(['green 3.5']) assert inp.green == 3.5 inp = r.read_input(['green none']) assert inp.green is None with raises(ReaderError) as e: inp = r.read_input(['green bird']) assert search(r'expected one of .+, got "\w+"', str(e.value)) r.add_line_key('cyan', type=[str, (None, str), float]) inp = r.read_input(['cyan cat dog 6']) assert inp.cyan == ('cat', 'dog', 6) with raises(ReaderError) as e: inp = r.read_input(['cyan cat dog 7 8']) assert search('expected .+, got \w+', str(e.value)) with raises(ReaderError) as e: inp = r.read_input(['cyan cat none bird']) assert search('expected \w+, got "\w+"', str(e.value)) inp = r.read_input(['cyan cat none 7.8']) assert inp.cyan == ('cat', None, 7.8) r.add_line_key('black', type=('hey', str('hi'), 4, 2.8)) inp = r.read_input(['black 4']) assert inp.black == 4 inp = r.read_input(['black 2.8']) assert inp.black == 2.8 inp = r.read_input(['black hey']) assert inp.black == 'hey' inp = r.read_input(['black hi']) assert inp.black == 'hi' with raises(ReaderError) as e: inp = r.read_input(['black whoops']) assert search('expected one of [a-zA-Z0-9, ".]+, got "\w+"', str(e.value)) with raises(ReaderError) as e: inp = r.read_input(['black 2']) assert search('expected one of [a-zA-Z0-9, ".]+, got "\d+"', str(e.value)) import re r.add_line_key('pink', type=re.compile(r'neat\d+')) inp = r.read_input(['pink neat68']) assert inp.pink == 'neat68' with raises(ReaderError) as e: inp = r.read_input(['pink near68']) assert search('expected regex\(.*\)', str(e.value))
def test_line_read_using_globs(): r = InputReader() r.add_line_key('blue', type=[int, int], glob={'len':'*', 'type':int}) inp = r.read_input(['blue 1 2 3 4 5 6 7']) assert inp.blue == (1, 2, 3, 4, 5, 6, 7) inp = r.read_input(['blue 1 2']) assert inp.blue == (1, 2) r.add_line_key('red', type=[int, int], glob={'len':'*', 'type':int, 'join':True}) inp = r.read_input(['red 1 2 3 4 5 6 7']) assert inp.red == (1, 2, '3 4 5 6 7') inp = r.read_input(['red 1 2']) assert inp.red == (1, 2) r.add_line_key('pink', type=[int, int], glob={'len':'+', 'type':float, 'join':True}) inp = r.read_input(['pink 1 2 3 4 5 6 7']) assert inp.pink == (1, 2, '3.0 4.0 5.0 6.0 7.0') inp = r.read_input(['pink 1 2 3']) assert inp.pink == (1, 2, '3.0') r.add_line_key('cyan', type=[int, int], glob={'len':'?', 'type':int}) inp = r.read_input(['cyan 1 2 3']) assert inp.cyan == (1, 2, 3) inp = r.read_input(['cyan 1 2']) assert inp.cyan == (1, 2) r.add_line_key('yellow', type=int, glob={'len':'?', 'type':int}) inp = r.read_input(['yellow 1 2']) assert inp.yellow == (1, 2) inp = r.read_input(['yellow 1']) assert inp.yellow == (1,) r.add_line_key('teal', type=int, glob={'len':'?', 'type':int, 'default':3}) inp = r.read_input(['teal 1 2']) assert inp.teal == (1, 2) inp = r.read_input(['teal 1']) assert inp.teal == (1, 3) r.add_line_key('gray', type=None, glob={'len':'?'}) inp = r.read_input(['gray bye']) assert inp.gray == 'bye' inp = r.read_input(['gray']) assert inp.gray == '' r.add_line_key('white', type=None, glob={'len':'*'}) inp = r.read_input(['white hi lo']) assert inp.white == ('hi', 'lo') inp = r.read_input(['white']) assert inp.white == () r.add_line_key('green', type=None, glob={'len':'+', 'join':True}) inp = r.read_input(['green hi lo']) assert inp.green == 'hi lo' r.add_line_key('orange', type=None, glob={'len':'*', 'join':True}) inp = r.read_input(['orange']) assert inp.orange == '' r.add_line_key('black', type=int, glob={'len':'?', 'type':int}) with raises(ReaderError) as e: r.read_input(['black 1 2 3']) assert search(r'expected at most \d+ arguments, got \d+', str(e.value)) r.add_line_key('maroon', type=int, glob={'len':'+', 'type':int}) with raises(ReaderError) as e: r.read_input(['maroon 1']) assert search(r'expected at least \d+ arguments, got \d+', str(e.value))
def read_input(input_file): '''Defines what to expect from the input file and then reads it in.''' # Creates an input reader instance reader = InputReader(default=SUPPRESS) # Rate parameter, either rate or lifetime, not both rate = reader.add_mutually_exclusive_group(required=True) # The units are s, ns, ps, or fs. The default is ps. rate.add_line_key('lifetime', type=float, glob={'len' : '?', 'type' : ('ps', 'fs', 'ns', 's'), 'default' : 'ps'}) rate.add_line_key('rate', type=float, glob={'len' : '?', 'type' : ('thz', 'phz', 'ghz', 'hz'), 'default' : 'thz'}) # The range of the X-axis reader.add_line_key('xlim', type=[int, int], default=(1900, 2000)) reader.add_boolean_key('reverse', action=True, default=False) # Read in the raw data. reader.add_line_key('raw', type=[], glob={'len':'*', 'join':True, }, default=None, case=True) # Read in the peak data. The wavenumber and height is required. # The Lorentzian and Gaussian widths are defaulted to 10 if not given. floatkw = {'type' : float, 'default' : 10.0} reader.add_line_key('peak', required=True, repeat=True, type=[float,float], keywords={'g':floatkw, 'l':floatkw, 'num' : {'type':int,'default':-1}}) # Read the exchange information. reader.add_line_key('exchange', repeat=True, type=[int, int], glob={'type' : float, 'default' : 1.0, 'len' : '?'}) reader.add_boolean_key('nosym', action=False, default=True, dest='symmetric_exchange') # Actually read the input file args = reader.read_input(input_file) # Make sure the filename was given correctly and read in data if args.raw: args.add('rawName', args.raw) args.raw = loadtxt(abs_file_path(args.raw)) # Make the output file path absolute if given args.data = abs_file_path(args.data) if 'data' in args else '' if 'save_plot_script' in args: args.save_plot_script = abs_file_path(args.save_plot_script) else: args.save_plot_script = '' # Adjust the input rate or lifetime to wavenumbers if 'lifetime' in args: convert = { 'ps' : 1E-12, 'ns' : 1E-9, 'fs' : 1E-15, 's' : 1 } args.add('k', 1 / ( convert[args.lifetime[1]] * args.lifetime[0] )) else: convert = { 'thz' : 1E12, 'ghz' : 1E9, 'phz' : 1E15, 'hz' : 1 } args.add('k', convert[args.rate[1]] * args.rate[0]) args.k *= HZ2WAVENUM / ( 2 * pi ) # Parse the vibrational input num, vib, Gamma_Lorentz, Gamma_Gauss, heights, rel_rates, num_given = ( [], [], [], [], [], [], []) for peak in args.peak: # Vibration # num.append(peak[2]['num']) num_given.append(False if peak[2]['num'] < 0 else True) # Angular frequency vib.append(peak[0]) # Relative peak heights heights.append(peak[1]) # Default Gaussian or Lorentzian width or relative rate Gamma_Lorentz.append(peak[2]['l']) Gamma_Gauss.append(peak[2]['g']) # Either all or none of the numbers must be given explicitly if not (all(num_given) or not any(num_given)): raise ReaderError('All or none of the peaks must ' 'be given numbers explicitly') # If the numbers were give, make sure there are no duplicates if all(num_given): if len(num) != len(set(num)): raise ReaderError('Duplicate peaks cannot be given') # If none were given, number automatically else: num = range(1, len(num)+1, 1) args.add('num', array(num)) args.add('vib', array(vib)) args.add('heights', array(heights)) args.add('Gamma_Lorentz', array(Gamma_Lorentz)) args.add('Gamma_Gauss', array(Gamma_Gauss)) # Set up the exchanges # Make sure the each exchange number appears in num. num = set(num) ex = [] rates = [] string = 'Requested peak {0} in exchange does not exist' if 'exchange' in args: for exchange in args.exchange: p1 = exchange[0] if p1 not in num: raise ReaderError(string.format(p1)) p2 = exchange[1] if p2 not in num: raise ReaderError(string.format(p2)) if p1 == p2 and args.symmetric_exchange: raise ReaderError('Self exchange is not allowed') rate = exchange[2] # Offset the peak number by one to match python indicies ex.append([p1-1, p2-1]) rates.append(rate) else: ex = [] rates = [] args.add('exchanges', array(ex, dtype=int)) args.add('exchange_rates', array(rates)) # Make sure the xlimits are ascending try: range_check(args.xlim[0], args.xlim[1]) except ValueError: raise ReaderError('In xrange, the low value must ' 'less than the high value') return args
def test_line_type_definitions(): # Make sure that correct types are OK r = InputReader() a = r.add_line_key('red', type=str) assert a._type == [str] s = r.add_line_key('blue', type=[int]) assert s._type == [int] t = r.add_line_key('green', type=(14, 2.8, None, 'hey', str('hello'), str)) assert t._type == [(14, 2.8, None, 'hey', 'hello', str)] with raises(ValueError) as e: u = r.add_line_key('gray', type=None) assert 'type, glob and keywords cannot all be empty' in str(e.value) v = r.add_line_key('cyan', type=[(int, float), str]) assert v._type == [(int, float), str] import re regex = re.compile(r'(\d+|\w*)') w = r.add_line_key('pink', type=regex) assert w._type == [regex] # Make sure incorrect types are not OK regex = re.compile(str(r'hi\s+bye')) with raises(ValueError) as e: r.add_line_key('black', type=regex) assert 'Regex should not allow the possibility of spaces' in str(e.value) regex = re.compile(r'hi.*bye') with raises(ValueError) as e: r.add_line_key('black', type=regex) assert 'Regex should not allow the possibility of spaces' in str(e.value) with raises(ValueError) as e: r.add_line_key('black', type="") assert 'String cannot be of zero length' in str(e.value) with raises(ValueError) as e: r.add_line_key('black', type='hey there') assert 'String cannot contain spaces' in str(e.value) with raises(ValueError) as e: r.add_line_key('black', type=str('hey there')) assert 'String cannot contain spaces' in str(e.value) with raises(ValueError) as e: r.add_line_key('black', type=set([str, int])) assert 'type must be one of' in str(e.value) with raises(ValueError) as e: r.add_line_key('black', type=complex) assert 'type must be one of' in str(e.value) with raises(ValueError) as e: r.add_line_key('black', type=[str, [str, int]]) assert r'Embedded lists not allowed in type' in str(e.value) with raises(ValueError) as e: r.add_line_key('black', type=[str, ()]) assert r'Empty tuple in type' in str(e.value)
def test_line_keyword_definitions(): # Test that keyword checking is OK r = InputReader() a = r.add_line_key('red', keywords={'rose':{}}) assert a._keywords == {'rose':{'default':SUPPRESS,'type':str}} # Make sure str works, not just unicode (python2.x) b = r.add_line_key('blue', keywords={str('rose'):None}) assert b._keywords == {'rose':{'default':SUPPRESS,'type':str}} c = r.add_line_key('pink', keywords={ 'elephant':{str('default'):'drunk'}, 'cadillac':{'default':str('bruce')}}) assert c._keywords == \ {'elephant':{'default':'drunk','type':str}, 'cadillac':{'default':'bruce','type':str}} # Test that keyword checking is OK for bad input with raises(ValueError) as e: r.add_line_key('cyan', keywords='wrong') assert 'keywords must be a dict' in str(e.value) with raises(TypeError) as e: r.add_line_key('cyan', keywords={'p':{'wrong':None}}) assert 'Unknown key in keyword' in str(e.value) with raises(ValueError) as e: r.add_line_key('cyan', keywords={23:None}) assert 'must be of type str' in str(e.value) with raises(ValueError) as e: r.add_line_key('cyan', keywords={'p':['things']}) assert search(r'Options for keyword "\w+" must be a dict', str(e.value)) with raises(ValueError) as e: r.add_line_key('cyan', keywords={'p':{'type':[str]}}) assert 'list not allowed in type' in str(e.value) with raises(ValueError) as e: r.add_line_key('cyan', keywords={'p':{'type':complex}}) assert 'type must be one of' in str(e.value)