diff --git a/tests/test_config.py b/tests/test_config.py index 1c4a3d9aa..f5d940242 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -21,6 +21,7 @@ import os import shutil import sys +import tempfile import unittest from tests.common import build_temp_workspace @@ -54,13 +55,16 @@ def test_unknown_rule(self): ' this-one-does-not-exist: enable\n') def test_missing_option(self): - with self.assertRaisesRegexp( - config.YamlLintConfigError, - 'invalid config: missing option "max-spaces-before" ' - 'for rule "colons"'): - config.YamlLintConfig('rules:\n' + c = config.YamlLintConfig('rules:\n' + ' colons: enable\n') + self.assertEqual(c.rules['colons']['max-spaces-before'], 0) + self.assertEqual(c.rules['colons']['max-spaces-after'], 1) + + c = config.YamlLintConfig('rules:\n' ' colons:\n' - ' max-spaces-after: 1\n') + ' max-spaces-before: 9\n') + self.assertEqual(c.rules['colons']['max-spaces-before'], 9) + self.assertEqual(c.rules['colons']['max-spaces-after'], 1) def test_unknown_option(self): with self.assertRaisesRegexp( @@ -111,17 +115,22 @@ def test_yes_no_for_booleans(self): ' indent-sequences: YES!\n' ' check-multi-line-strings: false\n') + def test_enable_disable_keywords(self): + c = config.YamlLintConfig('rules:\n' + ' colons: enable\n' + ' hyphens: disable\n') + self.assertEqual(c.rules['colons'], {'level': 'error', + 'max-spaces-after': 1, + 'max-spaces-before': 0}) + self.assertEqual(c.rules['hyphens'], False) + def test_validate_rule_conf(self): class Rule(object): ID = 'fake' self.assertFalse(config.validate_rule_conf(Rule, False)) - self.assertFalse(config.validate_rule_conf(Rule, 'disable')) - self.assertEqual(config.validate_rule_conf(Rule, {}), {'level': 'error'}) - self.assertEqual(config.validate_rule_conf(Rule, 'enable'), - {'level': 'error'}) config.validate_rule_conf(Rule, {'level': 'error'}) config.validate_rule_conf(Rule, {'level': 'warning'}) @@ -129,22 +138,22 @@ class Rule(object): config.validate_rule_conf, Rule, {'level': 'warn'}) Rule.CONF = {'length': int} + Rule.DEFAULT = {'length': 80} config.validate_rule_conf(Rule, {'length': 8}) - self.assertRaises(config.YamlLintConfigError, - config.validate_rule_conf, Rule, {}) + config.validate_rule_conf(Rule, {}) self.assertRaises(config.YamlLintConfigError, config.validate_rule_conf, Rule, {'height': 8}) Rule.CONF = {'a': bool, 'b': int} + Rule.DEFAULT = {'a': True, 'b': -42} config.validate_rule_conf(Rule, {'a': True, 'b': 0}) - self.assertRaises(config.YamlLintConfigError, - config.validate_rule_conf, Rule, {'a': True}) - self.assertRaises(config.YamlLintConfigError, - config.validate_rule_conf, Rule, {'b': 0}) + config.validate_rule_conf(Rule, {'a': True}) + config.validate_rule_conf(Rule, {'b': 0}) self.assertRaises(config.YamlLintConfigError, config.validate_rule_conf, Rule, {'a': 1, 'b': 0}) Rule.CONF = {'choice': (True, 88, 'str')} + Rule.DEFAULT = {'choice': 88} config.validate_rule_conf(Rule, {'choice': True}) config.validate_rule_conf(Rule, {'choice': 88}) config.validate_rule_conf(Rule, {'choice': 'str'}) @@ -156,8 +165,10 @@ class Rule(object): config.validate_rule_conf, Rule, {'choice': 'abc'}) Rule.CONF = {'choice': (int, 'hardcoded')} + Rule.DEFAULT = {'choice': 1337} config.validate_rule_conf(Rule, {'choice': 42}) config.validate_rule_conf(Rule, {'choice': 'hardcoded'}) + config.validate_rule_conf(Rule, {}) self.assertRaises(config.YamlLintConfigError, config.validate_rule_conf, Rule, {'choice': False}) self.assertRaises(config.YamlLintConfigError, @@ -165,7 +176,7 @@ class Rule(object): class ExtendedConfigTestCase(unittest.TestCase): - def test_extend_add_rule(self): + def test_extend_on_object(self): old = config.YamlLintConfig('rules:\n' ' colons:\n' ' max-spaces-before: 0\n' @@ -182,60 +193,130 @@ def test_extend_add_rule(self): self.assertEqual(len(new.enabled_rules(None)), 2) - def test_extend_remove_rule(self): - old = config.YamlLintConfig('rules:\n' - ' colons:\n' - ' max-spaces-before: 0\n' - ' max-spaces-after: 1\n' - ' hyphens:\n' - ' max-spaces-after: 2\n') - new = config.YamlLintConfig('rules:\n' - ' colons: disable\n') - new.extend(old) + def test_extend_on_file(self): + with tempfile.NamedTemporaryFile('w') as f: + f.write('rules:\n' + ' colons:\n' + ' max-spaces-before: 0\n' + ' max-spaces-after: 1\n') + f.flush() + c = config.YamlLintConfig('extends: ' + f.name + '\n' + 'rules:\n' + ' hyphens:\n' + ' max-spaces-after: 2\n') + + self.assertEqual(sorted(c.rules.keys()), ['colons', 'hyphens']) + self.assertEqual(c.rules['colons']['max-spaces-before'], 0) + self.assertEqual(c.rules['colons']['max-spaces-after'], 1) + self.assertEqual(c.rules['hyphens']['max-spaces-after'], 2) + + self.assertEqual(len(c.enabled_rules(None)), 2) - self.assertEqual(sorted(new.rules.keys()), ['colons', 'hyphens']) - self.assertFalse(new.rules['colons']) - self.assertEqual(new.rules['hyphens']['max-spaces-after'], 2) - - self.assertEqual(len(new.enabled_rules(None)), 1) + def test_extend_remove_rule(self): + with tempfile.NamedTemporaryFile('w') as f: + f.write('rules:\n' + ' colons:\n' + ' max-spaces-before: 0\n' + ' max-spaces-after: 1\n' + ' hyphens:\n' + ' max-spaces-after: 2\n') + f.flush() + c = config.YamlLintConfig('extends: ' + f.name + '\n' + 'rules:\n' + ' colons: disable\n') + + self.assertEqual(sorted(c.rules.keys()), ['colons', 'hyphens']) + self.assertFalse(c.rules['colons']) + self.assertEqual(c.rules['hyphens']['max-spaces-after'], 2) + + self.assertEqual(len(c.enabled_rules(None)), 1) def test_extend_edit_rule(self): - old = config.YamlLintConfig('rules:\n' - ' colons:\n' - ' max-spaces-before: 0\n' - ' max-spaces-after: 1\n' - ' hyphens:\n' - ' max-spaces-after: 2\n') - new = config.YamlLintConfig('rules:\n' - ' colons:\n' - ' max-spaces-before: 3\n' - ' max-spaces-after: 4\n') - new.extend(old) - - self.assertEqual(sorted(new.rules.keys()), ['colons', 'hyphens']) - self.assertEqual(new.rules['colons']['max-spaces-before'], 3) - self.assertEqual(new.rules['colons']['max-spaces-after'], 4) - self.assertEqual(new.rules['hyphens']['max-spaces-after'], 2) - - self.assertEqual(len(new.enabled_rules(None)), 2) + with tempfile.NamedTemporaryFile('w') as f: + f.write('rules:\n' + ' colons:\n' + ' max-spaces-before: 0\n' + ' max-spaces-after: 1\n' + ' hyphens:\n' + ' max-spaces-after: 2\n') + f.flush() + c = config.YamlLintConfig('extends: ' + f.name + '\n' + 'rules:\n' + ' colons:\n' + ' max-spaces-before: 3\n' + ' max-spaces-after: 4\n') + + self.assertEqual(sorted(c.rules.keys()), ['colons', 'hyphens']) + self.assertEqual(c.rules['colons']['max-spaces-before'], 3) + self.assertEqual(c.rules['colons']['max-spaces-after'], 4) + self.assertEqual(c.rules['hyphens']['max-spaces-after'], 2) + + self.assertEqual(len(c.enabled_rules(None)), 2) def test_extend_reenable_rule(self): - old = config.YamlLintConfig('rules:\n' - ' colons:\n' - ' max-spaces-before: 0\n' - ' max-spaces-after: 1\n' - ' hyphens: disable\n') - new = config.YamlLintConfig('rules:\n' - ' hyphens:\n' - ' max-spaces-after: 2\n') - new.extend(old) - - self.assertEqual(sorted(new.rules.keys()), ['colons', 'hyphens']) - self.assertEqual(new.rules['colons']['max-spaces-before'], 0) - self.assertEqual(new.rules['colons']['max-spaces-after'], 1) - self.assertEqual(new.rules['hyphens']['max-spaces-after'], 2) - - self.assertEqual(len(new.enabled_rules(None)), 2) + with tempfile.NamedTemporaryFile('w') as f: + f.write('rules:\n' + ' colons:\n' + ' max-spaces-before: 0\n' + ' max-spaces-after: 1\n' + ' hyphens: disable\n') + f.flush() + c = config.YamlLintConfig('extends: ' + f.name + '\n' + 'rules:\n' + ' hyphens:\n' + ' max-spaces-after: 2\n') + + self.assertEqual(sorted(c.rules.keys()), ['colons', 'hyphens']) + self.assertEqual(c.rules['colons']['max-spaces-before'], 0) + self.assertEqual(c.rules['colons']['max-spaces-after'], 1) + self.assertEqual(c.rules['hyphens']['max-spaces-after'], 2) + + self.assertEqual(len(c.enabled_rules(None)), 2) + + def test_extend_recursive_default_values(self): + with tempfile.NamedTemporaryFile('w') as f: + f.write('rules:\n' + ' braces:\n' + ' max-spaces-inside: 1248\n') + f.flush() + c = config.YamlLintConfig('extends: ' + f.name + '\n' + 'rules:\n' + ' braces:\n' + ' min-spaces-inside-empty: 2357\n') + + self.assertEqual(c.rules['braces']['min-spaces-inside'], 0) + self.assertEqual(c.rules['braces']['max-spaces-inside'], 1248) + self.assertEqual(c.rules['braces']['min-spaces-inside-empty'], 2357) + self.assertEqual(c.rules['braces']['max-spaces-inside-empty'], -1) + + with tempfile.NamedTemporaryFile('w') as f: + f.write('rules:\n' + ' colons:\n' + ' max-spaces-before: 1337\n') + f.flush() + c = config.YamlLintConfig('extends: ' + f.name + '\n' + 'rules:\n' + ' colons: enable\n') + + self.assertEqual(c.rules['colons']['max-spaces-before'], 1337) + self.assertEqual(c.rules['colons']['max-spaces-after'], 1) + + with tempfile.NamedTemporaryFile('w') as f1, \ + tempfile.NamedTemporaryFile('w') as f2: + f1.write('rules:\n' + ' colons:\n' + ' max-spaces-before: 1337\n') + f1.flush() + f2.write('extends: ' + f1.name + '\n' + 'rules:\n' + ' colons: disable\n') + f2.flush() + c = config.YamlLintConfig('extends: ' + f2.name + '\n' + 'rules:\n' + ' colons: enable\n') + + self.assertEqual(c.rules['colons']['max-spaces-before'], 0) + self.assertEqual(c.rules['colons']['max-spaces-after'], 1) class ExtendedLibraryConfigTestCase(unittest.TestCase): @@ -267,6 +348,9 @@ def test_extend_config_override_whole_rule(self): self.assertEqual(sorted(new.rules.keys()), sorted(old.rules.keys())) for rule in new.rules: self.assertEqual(new.rules[rule], old.rules[rule]) + self.assertEqual(new.rules['empty-lines']['max'], 42) + self.assertEqual(new.rules['empty-lines']['max-start'], 43) + self.assertEqual(new.rules['empty-lines']['max-end'], 44) def test_extend_config_override_rule_partly(self): old = config.YamlLintConfig('extends: default') @@ -280,6 +364,9 @@ def test_extend_config_override_rule_partly(self): self.assertEqual(sorted(new.rules.keys()), sorted(old.rules.keys())) for rule in new.rules: self.assertEqual(new.rules[rule], old.rules[rule]) + self.assertEqual(new.rules['empty-lines']['max'], 2) + self.assertEqual(new.rules['empty-lines']['max-start'], 42) + self.assertEqual(new.rules['empty-lines']['max-end'], 0) class IgnorePathConfigTestCase(unittest.TestCase): diff --git a/yamllint/conf/default.yaml b/yamllint/conf/default.yaml index fcece4d8e..da9701ea3 100644 --- a/yamllint/conf/default.yaml +++ b/yamllint/conf/default.yaml @@ -1,59 +1,28 @@ --- rules: - braces: - min-spaces-inside: 0 - max-spaces-inside: 0 - min-spaces-inside-empty: -1 - max-spaces-inside-empty: -1 - brackets: - min-spaces-inside: 0 - max-spaces-inside: 0 - min-spaces-inside-empty: -1 - max-spaces-inside-empty: -1 - colons: - max-spaces-before: 0 - max-spaces-after: 1 - commas: - max-spaces-before: 0 - min-spaces-after: 1 - max-spaces-after: 1 + braces: enable + brackets: enable + colons: enable + commas: enable comments: level: warning - require-starting-space: true - min-spaces-from-content: 2 comments-indentation: level: warning document-end: disable document-start: level: warning - present: true - empty-lines: - max: 2 - max-start: 0 - max-end: 0 - quoted-strings: disable - empty-values: - forbid-in-block-mappings: false - forbid-in-flow-mappings: false - hyphens: - max-spaces-after: 1 - indentation: - spaces: consistent - indent-sequences: true - check-multi-line-strings: false + empty-lines: enable + empty-values: enable + hyphens: enable + indentation: enable key-duplicates: enable key-ordering: disable - line-length: - max: 80 - allow-non-breakable-words: true - allow-non-breakable-inline-mappings: false + line-length: enable new-line-at-end-of-file: enable - new-lines: - type: unix - octal-values: - forbid-implicit-octal: false - forbid-explicit-octal: false + new-lines: enable + octal-values: enable + quoted-strings: disable trailing-spaces: enable truthy: level: warning diff --git a/yamllint/config.py b/yamllint/config.py index 3a3f3c777..b4a7c1d53 100644 --- a/yamllint/config.py +++ b/yamllint/config.py @@ -74,6 +74,11 @@ def parse(self, raw_content): raise YamlLintConfigError('invalid config: not a dict') self.rules = conf.get('rules', {}) + for rule in self.rules: + if self.rules[rule] == 'enable': + self.rules[rule] = {} + elif self.rules[rule] == 'disable': + self.rules[rule] = False # Does this conf override another conf that we need to load? if 'extends' in conf: @@ -102,10 +107,8 @@ def validate(self): def validate_rule_conf(rule, conf): - if conf is False or conf == 'disable': + if conf is False: # disable return False - elif conf == 'enable': - conf = {} if isinstance(conf, dict): if ('ignore' in conf and @@ -123,6 +126,7 @@ def validate_rule_conf(rule, conf): 'invalid config: level should be "error" or "warning"') options = getattr(rule, 'CONF', {}) + options_default = getattr(rule, 'DEFAULT', {}) for optkey in conf: if optkey in ('ignore', 'level'): continue @@ -143,9 +147,7 @@ def validate_rule_conf(rule, conf): % (optkey, rule.ID, options[optkey].__name__)) for optkey in options: if optkey not in conf: - raise YamlLintConfigError( - 'invalid config: missing option "%s" for rule "%s"' % - (optkey, rule.ID)) + conf[optkey] = options_default[optkey] else: raise YamlLintConfigError(('invalid config: rule "%s": should be ' 'either "enable", "disable" or a dict') diff --git a/yamllint/rules/braces.py b/yamllint/rules/braces.py index 59d955875..654b36d33 100644 --- a/yamllint/rules/braces.py +++ b/yamllint/rules/braces.py @@ -101,6 +101,10 @@ 'max-spaces-inside': int, 'min-spaces-inside-empty': int, 'max-spaces-inside-empty': int} +DEFAULT = {'min-spaces-inside': 0, + 'max-spaces-inside': 0, + 'min-spaces-inside-empty': -1, + 'max-spaces-inside-empty': -1} def check(conf, token, prev, next, nextnext, context): diff --git a/yamllint/rules/brackets.py b/yamllint/rules/brackets.py index 33bdaa920..b54c5154a 100644 --- a/yamllint/rules/brackets.py +++ b/yamllint/rules/brackets.py @@ -102,6 +102,10 @@ 'max-spaces-inside': int, 'min-spaces-inside-empty': int, 'max-spaces-inside-empty': int} +DEFAULT = {'min-spaces-inside': 0, + 'max-spaces-inside': 0, + 'min-spaces-inside-empty': -1, + 'max-spaces-inside-empty': -1} def check(conf, token, prev, next, nextnext, context): diff --git a/yamllint/rules/colons.py b/yamllint/rules/colons.py index 8d386b84f..fd46bef94 100644 --- a/yamllint/rules/colons.py +++ b/yamllint/rules/colons.py @@ -79,6 +79,8 @@ TYPE = 'token' CONF = {'max-spaces-before': int, 'max-spaces-after': int} +DEFAULT = {'max-spaces-before': 0, + 'max-spaces-after': 1} def check(conf, token, prev, next, nextnext, context): diff --git a/yamllint/rules/commas.py b/yamllint/rules/commas.py index ba423739a..bb7304454 100644 --- a/yamllint/rules/commas.py +++ b/yamllint/rules/commas.py @@ -103,6 +103,9 @@ CONF = {'max-spaces-before': int, 'min-spaces-after': int, 'max-spaces-after': int} +DEFAULT = {'max-spaces-before': 0, + 'min-spaces-after': 1, + 'max-spaces-after': 1} def check(conf, token, prev, next, nextnext, context): diff --git a/yamllint/rules/comments.py b/yamllint/rules/comments.py index 69da04529..7ecce39bc 100644 --- a/yamllint/rules/comments.py +++ b/yamllint/rules/comments.py @@ -68,6 +68,8 @@ TYPE = 'comment' CONF = {'require-starting-space': bool, 'min-spaces-from-content': int} +DEFAULT = {'require-starting-space': True, + 'min-spaces-from-content': 2} def check(conf, comment): diff --git a/yamllint/rules/document_end.py b/yamllint/rules/document_end.py index 8335a0548..e98aac1d1 100644 --- a/yamllint/rules/document_end.py +++ b/yamllint/rules/document_end.py @@ -82,6 +82,7 @@ ID = 'document-end' TYPE = 'token' CONF = {'present': bool} +DEFAULT = {'present': True} def check(conf, token, prev, next, nextnext, context): diff --git a/yamllint/rules/document_start.py b/yamllint/rules/document_start.py index 090260a07..36c3d8e8d 100644 --- a/yamllint/rules/document_start.py +++ b/yamllint/rules/document_start.py @@ -72,6 +72,7 @@ ID = 'document-start' TYPE = 'token' CONF = {'present': bool} +DEFAULT = {'present': True} def check(conf, token, prev, next, nextnext, context): diff --git a/yamllint/rules/empty_lines.py b/yamllint/rules/empty_lines.py index 93f4df417..335b1251b 100644 --- a/yamllint/rules/empty_lines.py +++ b/yamllint/rules/empty_lines.py @@ -58,6 +58,9 @@ CONF = {'max': int, 'max-start': int, 'max-end': int} +DEFAULT = {'max': 2, + 'max-start': 0, + 'max-end': 0} def check(conf, line): diff --git a/yamllint/rules/empty_values.py b/yamllint/rules/empty_values.py index daa62b23a..14bd0e042 100644 --- a/yamllint/rules/empty_values.py +++ b/yamllint/rules/empty_values.py @@ -75,6 +75,8 @@ TYPE = 'token' CONF = {'forbid-in-block-mappings': bool, 'forbid-in-flow-mappings': bool} +DEFAULT = {'forbid-in-block-mappings': False, + 'forbid-in-flow-mappings': False} def check(conf, token, prev, next, nextnext, context): diff --git a/yamllint/rules/hyphens.py b/yamllint/rules/hyphens.py index 1d5a1a425..df38b4c51 100644 --- a/yamllint/rules/hyphens.py +++ b/yamllint/rules/hyphens.py @@ -76,6 +76,7 @@ ID = 'hyphens' TYPE = 'token' CONF = {'max-spaces-after': int} +DEFAULT = {'max-spaces-after': 1} def check(conf, token, prev, next, nextnext, context): diff --git a/yamllint/rules/indentation.py b/yamllint/rules/indentation.py index cf4ebc5fc..8bb71751f 100644 --- a/yamllint/rules/indentation.py +++ b/yamllint/rules/indentation.py @@ -201,6 +201,9 @@ CONF = {'spaces': (int, 'consistent'), 'indent-sequences': (bool, 'whatever', 'consistent'), 'check-multi-line-strings': bool} +DEFAULT = {'spaces': 'consistent', + 'indent-sequences': True, + 'check-multi-line-strings': False} ROOT, B_MAP, F_MAP, B_SEQ, F_SEQ, B_ENT, KEY, VAL = range(8) labels = ('ROOT', 'B_MAP', 'F_MAP', 'B_SEQ', 'F_SEQ', 'B_ENT', 'KEY', 'VAL') diff --git a/yamllint/rules/key_duplicates.py b/yamllint/rules/key_duplicates.py index fc9c5a36b..bd38b1434 100644 --- a/yamllint/rules/key_duplicates.py +++ b/yamllint/rules/key_duplicates.py @@ -61,7 +61,6 @@ ID = 'key-duplicates' TYPE = 'token' -CONF = {} MAP, SEQ = range(2) diff --git a/yamllint/rules/key_ordering.py b/yamllint/rules/key_ordering.py index 3bd93c7c7..1ca992b66 100644 --- a/yamllint/rules/key_ordering.py +++ b/yamllint/rules/key_ordering.py @@ -72,7 +72,6 @@ ID = 'key-ordering' TYPE = 'token' -CONF = {} MAP, SEQ = range(2) diff --git a/yamllint/rules/line_length.py b/yamllint/rules/line_length.py index f7a2f2f8f..9b5a1ab68 100644 --- a/yamllint/rules/line_length.py +++ b/yamllint/rules/line_length.py @@ -102,6 +102,9 @@ CONF = {'max': int, 'allow-non-breakable-words': bool, 'allow-non-breakable-inline-mappings': bool} +DEFAULT = {'max': 80, + 'allow-non-breakable-words': True, + 'allow-non-breakable-inline-mappings': False} def check_inline_mapping(line): diff --git a/yamllint/rules/new_lines.py b/yamllint/rules/new_lines.py index 91adb4ef8..3aae90fe3 100644 --- a/yamllint/rules/new_lines.py +++ b/yamllint/rules/new_lines.py @@ -30,6 +30,7 @@ ID = 'new-lines' TYPE = 'line' CONF = {'type': ('unix', 'dos')} +DEFAULT = {'type': 'unix'} def check(conf, line): diff --git a/yamllint/rules/octal_values.py b/yamllint/rules/octal_values.py index 33c079308..40de39a61 100644 --- a/yamllint/rules/octal_values.py +++ b/yamllint/rules/octal_values.py @@ -66,6 +66,8 @@ TYPE = 'token' CONF = {'forbid-implicit-octal': bool, 'forbid-explicit-octal': bool} +DEFAULT = {'forbid-implicit-octal': False, + 'forbid-explicit-octal': False} def check(conf, token, prev, next, nextnext, context): diff --git a/yamllint/rules/quoted_strings.py b/yamllint/rules/quoted_strings.py index 711a62757..b01926199 100644 --- a/yamllint/rules/quoted_strings.py +++ b/yamllint/rules/quoted_strings.py @@ -46,6 +46,7 @@ ID = 'quoted-strings' TYPE = 'token' CONF = {'quote-type': ('any', 'single', 'double')} +DEFAULT = {'quote-type': 'any'} def check(conf, token, prev, next, nextnext, context): diff --git a/yamllint/rules/truthy.py b/yamllint/rules/truthy.py index c14b06ae8..b738dc229 100644 --- a/yamllint/rules/truthy.py +++ b/yamllint/rules/truthy.py @@ -71,7 +71,6 @@ ID = 'truthy' TYPE = 'token' -CONF = {} TRUTHY = ['YES', 'Yes', 'yes', 'NO', 'No', 'no',