def determine_style(configuration: Configuration, style_name: Optional[str] = None) -> Style: available_styles = configuration.available_styles() if style_name is None: if len(available_styles) == 1: style_name = available_styles[0] else: message = "Cannot pick a default style from file containing " \ "{0} styles" raise StylistException(message.format(len(available_styles))) if style_name not in available_styles: message = f"style '{style_name}' not found in configuration" raise StylistException(message) # Todo: It would be nice to remove abstracts from this list but I haven't # worked out how yet. # potential_rules: Dict[str, Type[stylist.rule.Rule]] \ = {cls.__name__: cls for cls in _all_subclasses(stylist.rule.Rule)} rules: List[stylist.rule.Rule] = [] rule_list = configuration.get_style(style_name) if not isinstance(rule_list, list): raise TypeError('Style rules should be a list of names') for rule_description in rule_list: rule_name, _, rule_arguments_string = rule_description.partition('(') rule_name = rule_name.strip() rule_arguments_string, _, _ = rule_arguments_string.partition(')') rule_arguments: List[str] = [] if rule_arguments_string.strip(): rule_arguments = [thing.strip() for thing in rule_arguments_string.split(',')] if rule_name not in potential_rules: raise StylistException(f"Unrecognised rule: {rule_name}") if rule_arguments: processed_args: List[str] = [] processed_kwargs: Dict[str, str] = {} for arg in rule_arguments: match = _ARGUMENT_PATTERN.match(arg) if match is None: message = "Failed to comprehend rule argument list" raise StylistException(message) if match.group(1) is not None: processed_kwargs[match.group(1)] = eval(match.group(2)) else: processed_args.append(eval(match.group(2))) # TODO: Currently the use of *args and **kwargs here confuses mypy. # new_rule = potential_rules[rule_name]( # type: ignore *processed_args, **processed_kwargs) rules.append(new_rule) else: rules.append(potential_rules[rule_name]()) return Style(rules)
def test_get_pipe(self) -> None: input = {'file-pipe': {'f90': 'fortran', 'F90': 'fortran:fpp', 'x90': 'fortran:pfp:fpp'}} expected = [('f90', FortranSource, []), ('F90', FortranSource, [FortranPreProcessor]), ('x90', FortranSource, [PFUnitProcessor, FortranPreProcessor])] test_unit = Configuration(input) assert expected == list(test_unit.get_file_pipes())
def test_configuration(self, style_file) -> None: test_unit = Configuration(style_file) expected = [key[6:] for key in style_file.keys() if key.startswith('style.')] assert test_unit.available_styles() == expected for key in style_file.keys(): if key.startswith('style.'): expected = _RULE_PATTERN.findall(style_file[key]['rules']) expected = [item.strip() for item in expected] assert test_unit.get_style(key[6:]) == expected
def test_parse_pipe(self, pipe_string) -> None: stimulus, expected = pipe_string if expected is not None: extension, source, preproc \ = Configuration.parse_pipe_description(stimulus) assert expected[0] == extension assert expected[1] == source assert expected[2] == preproc else: with raises(StylistException): _ = Configuration.parse_pipe_description(stimulus)
def test_none(self): """ Checks that an error is thrown if an attempt is made to get a style from configuration which contains none. """ no_style_conf = Configuration({'': {'a': 42}}) with pytest.raises(StylistException): stylist.style.determine_style(no_style_conf, 'wibble')
def test_no_default(self, tmp_path: Path): """ Checks that an error is thrown if an attempt is made to load a default style from an empty style list. """ empty_conf = Configuration({'cheese': {'a': '42'}}) with pytest.raises(StylistException): stylist.style.determine_style(empty_conf)
def test_default_style(self, tmp_path: Path): """ Checks that the only style is loaded if none is specified. """ single_style_conf = Configuration( {'cheese': {'thingy': 'thangy'}, 'style.maybe': {'rules': "_RuleHarnessOne, _RuleHarnessTwo('blah')"}}) new_style = stylist.style.determine_style(single_style_conf) assert self._name_rules(new_style) == ['_RuleHarnessOne', '_RuleHarnessTwo']
def test_raw_argument(self, tmp_path: Path): """ Checks that raw-string arguments are handled correctly. """ initialiser = {'style.rawarg': {'rules': "_RuleHarnessTwo(r'.*')"}} conf = Configuration(initialiser) style = stylist.style.determine_style(conf) rules = style.list_rules() assert len(rules) == 1 assert isinstance(rules[0], _RuleHarnessTwo) assert cast(_RuleHarnessTwo, rules[0]).thing == r'.*'
def test_single_style(self, tmp_path: Path): """ Checks that a single style can be extracted from a list of one. """ single_style_conf = Configuration( {'cheese': {'thingy': 'thangy'}, 'style.singular': {'rules': "_RuleHarnessOne, _RuleHarnessTwo('blah')"}}) new_style = stylist.style.determine_style(single_style_conf, 'singular') assert self._name_rules(new_style) == ['_RuleHarnessOne', '_RuleHarnessTwo']
def test_ambiguous_default(self, tmp_path: Path): """ Checks that an error is thrown if no style is specified but several are available. """ several_style_conf = Configuration( {'style.the_first': {'rules': '_RuleHarnessOne'}, 'style.the_second': {'rules': '_RuleHarnessOne, _RuleHarnessTwo(42)'}, 'beef': {'whatsits': 'cheesy'}, 'style.the_third': {'rules': "_RuleHarnessTwo('super')"}}) with pytest.raises(StylistException): stylist.style.determine_style(several_style_conf)
def test_one_of_several(self, tmp_path: Path): """ Checks that a single style can be extracted from several alternatives. """ several_style_conf = Configuration( {'style.the_first': {'rules': '_RuleHarnessOne'}, 'style.the_second': {'rules': '_RuleHarnessOne, _RuleHarnessTwo(42)'}, 'beef': {'whatsits': 'cheesy'}, 'style.the_third': {'rules': "_RuleHarnessTwo('super')"}}) new_style = stylist.style.determine_style(several_style_conf, 'the_second') assert self._name_rules(new_style) == ['_RuleHarnessOne', '_RuleHarnessTwo']
def test_style_with_empty_rules(self) -> None: test_unit = Configuration({'style.empty-rules': {'rules': ''}}) assert test_unit.available_styles() == ['empty-rules'] with raises(StylistException): _ = test_unit.get_style('empty-rules')
def test_style_without_rules(self) -> None: test_unit = Configuration({'style.no-rules': {'only': 'thing'}}) assert test_unit.available_styles() == ['no-rules'] with raises(KeyError): _ = test_unit.get_style('no-rules')
def test_empty_style(self) -> None: test_unit = Configuration({'style.empty': {}}) assert test_unit.available_styles() == ['empty'] with raises(KeyError): _ = test_unit.get_style('empty')
def test_no_styles(self) -> None: test_unit = Configuration({'no-style': {}}) assert test_unit.available_styles() == []
def test_empty_file(self) -> None: test_unit = Configuration({}) assert test_unit.available_styles() == []
def test_raw_rule_arguments(self) -> None: initialiser = {'style.raw-args': {'rules': 'rule(r\'.*\')'}} test_unit = Configuration(initialiser) assert test_unit.available_styles() == ['raw-args'] assert test_unit.get_style('raw-args') == ['rule(r\'.*\')']
def test_no_pipe(self) -> None: test_unit = Configuration({}) assert [] == test_unit.get_file_pipes()