from fastcore.test import *arguments
This module is meant to add all the support functions for arguments and inout parsing.
Find support function
optional_find let you search a string in forward or reverse order using one or more templates (aka: reference sub-strings).
It returns the first occurrence that is the leftmost in forward direction and rightmost in reverse direction. The occurrence returned is a tuple (position,template) where: + position: is the position inside the string. + template: is the copy of the template found. We need this to understand where that string finishes.
optional_find
optional_find (x, cc, reverse=False)
# Single
test_eq( optional_find('abc','a')[0] , 0)
test_eq( optional_find('abc','c')[0] , 2)
test_eq( optional_find('abc','bc')[0] , 1)
test_eq( optional_find('abc','z'), None)
test_eq( optional_find('abcdefghiab','a',reverse=True)[0] , 9)
test_eq( optional_find('abc','bc',reverse=True)[0] , 1)
# Multi
test_eq( optional_find('abcdefghiab',['aa','fg','ia'],reverse=False)[0] , 5)
test_eq( optional_find('abcdefghiab',['aa','fg','ia'],reverse=True)[0] , 8)
test_eq( optional_find('abcdefghiab',['aa','ia','fg'],reverse=True)[0] , 8)Character level utility
count_char
count_char (x, c)
test_eq( count_char('abacda','a') , 3 )
test_eq( count_char('abacda','b') , 1 )
test_eq( count_char('abacda','z') , 0 )count_delta
count_delta (x, a='(', b=')')
test_eq(count_delta('asd(asd)'),0)
test_eq(count_delta('asd(asd'),1)
test_eq(count_delta('asd(a(sd'),2)
test_eq(count_delta('asd(a(sd)))'),-1)String split utility
This is just a bit of syntactic sugar
split_and_strip
split_and_strip (x, splitter)
test_eq(split_and_strip('a,b,c',','),['a','b','c'])
test_eq(split_and_strip(' a, b,c ',','),['a','b','c'])
test_eq(split_and_strip('',','),[])Inout util
inout is the name of the portion of the complete arguments string that refers to input and output parameters.
For example in this magick line: %%testcell noglobals (aaa,bbb) ->(ccc) + raw_arguments: noglobals + inout: (aaa,bbb) ->(ccc)
process_inout
process_inout (x, splitter='->')
test_eq(process_inout(None,splitter='->'),None)
test_eq(process_inout('(a,b,cc)->(d,ee)',splitter='->'),(['a','b','cc'],['d','ee']))
test_eq(process_inout(' (a,b,cc) -> (d,ee) ',splitter='->'),(['a','b','cc'],['d','ee']))
test_eq(process_inout(' (a,b,cc) -> () ',splitter='->'),(['a','b','cc'],[]))
test_eq(process_inout(' (a,b,cc) ',splitter='->'),(['a','b','cc'],[]))
test_eq(process_inout('()->(a,b,cc) ',splitter='->'),([],['a','b','cc']))
test_eq(process_inout('->(a,b,cc) ',splitter='->'),([],['a','b','cc']))
test_fail(lambda: process_inout('(a,b,cc)(d,ee)'), contains='Too much parenthesis')
test_fail(lambda: process_inout('(a,b,cc) (d,ee)'), contains='Too much parenthesis')
test_fail(lambda: process_inout('(a,b,cc) (->d,ee)'), contains='Too much parenthesis')
test_fail(lambda: process_inout('(a,b,cc->)(d,ee)'), contains='Missing closing parenthesis')
test_fail(lambda: process_inout('(a,b,cc) - > (d,ee)'), contains='Too much parenthesis')
test_fail(lambda: process_inout('(a,b,cc) ? (d,ee)'), contains='Too much parenthesis')separate_args_and_inout
separate_args_and_inout (x)
test_eq(separate_args_and_inout(None), None) # None input → None output
test_eq(separate_args_and_inout(''), ['', None]) # Empty string → empty args, no in/out
test_eq(separate_args_and_inout('verbose'), ['verbose', None]) # Only args, no in/out
test_eq(separate_args_and_inout('dryrun verbose'), ['dryrun verbose', None]) # Multiple args, no in/out
test_eq(separate_args_and_inout('(a,b)->(c,d)'), ['', '(a,b)->(c,d)']) # Pure in/out spec, no args
test_eq(separate_args_and_inout('(a,b)'), ['', '(a,b)']) # Only input tuple, no args
test_eq(separate_args_and_inout('->(c,d)'), ['', '->(c,d)']) # Only output tuple, no args
test_eq(separate_args_and_inout('dryrun verbose (a,b)'), ['dryrun verbose ', '(a,b)']) # Args + input tuple
test_eq(separate_args_and_inout('dryrun verbose (a,b) -> (c,d) '), ['dryrun verbose ', '(a,b) -> (c,d)']) # Args + in/out
test_eq(separate_args_and_inout('dryrun verbose () -> (c,d) '), ['dryrun verbose ', '() -> (c,d)']) # Args + empty input tuple
test_eq(separate_args_and_inout('dryrun verbose (a,b) -> ()'), ['dryrun verbose ', '(a,b) -> ()']) # Args + empty output tuple
test_eq(separate_args_and_inout('dryrun verbose (a,b) '), ['dryrun verbose ', '(a,b)']) # Args + input tuple, trailing spaces
test_eq(separate_args_and_inout('dryrun (a,b) verbose '), ['dryrun verbose ', '(a,b)']) # In/out tuple between args
test_eq(separate_args_and_inout('dryrun (a,b) -> (c,d) verbose'), ['dryrun verbose', '(a,b) -> (c,d)']) # In/out in middle of args
test_eq(separate_args_and_inout('dryrun () -> (c,d) verbose'), ['dryrun verbose', '() -> (c,d)']) # Empty input tuple
test_eq(separate_args_and_inout('dryrun () -> (c,d)verbose'), ['dryrun verbose', '() -> (c,d)']) # No space before verbose
test_eq(separate_args_and_inout('dryrun() -> (c,d) verbose'), ['dryrun verbose', '() -> (c,d)']) # No space before ()
test_eq(separate_args_and_inout('dryrun (a,b)->() verbose'), ['dryrun verbose', '(a,b)->()']) # Compact arrow, spacing preserved
test_eq(separate_args_and_inout('dryrun (a, b) verbose'), ['dryrun verbose', '(a, b)']) # Tuple spacing preserved
test_eq(separate_args_and_inout('dryrun -> (c,d)verbose'), ['dryrun verbose', '-> (c,d)']) # Output tuple glued to arg
test_eq(separate_args_and_inout('dryrun (c,d)->verbose'), ['dryrun verbose', '(c,d)->']) # Input tuple glued to arg
test_eq(separate_args_and_inout('dryrun (incomplete'), ['dryrun (incomplete', None]) # Unclosed parenthesis → ignore as in/outConsume inout
validate_and_update_inputs
validate_and_update_inputs (inputs:list, state:dict)
test_eq( validate_and_update_inputs(['a'],{'a':1, 'b':2}) , {'a':1} )
test_fail(lambda: validate_and_update_inputs(['a'],{'b':1}) , contains='not found: a' )
test_fail(lambda: validate_and_update_inputs(['a','c'],{'b':1}) , contains='not found: a, c' )