Hello community, here is the log from the commit of package python-yamllint for openSUSE:Leap:15.2 checked in at 2020-04-20 12:55:36 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Leap:15.2/python-yamllint (Old) and /work/SRC/openSUSE:Leap:15.2/.python-yamllint.new.2738 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-yamllint" Mon Apr 20 12:55:36 2020 rev:6 rq:795560 version:1.22.1 Changes: -------- --- /work/SRC/openSUSE:Leap:15.2/python-yamllint/python-yamllint.changes 2020-03-02 13:25:09.330612875 +0100 +++ /work/SRC/openSUSE:Leap:15.2/.python-yamllint.new.2738/python-yamllint.changes 2020-04-20 12:55:49.880760744 +0200 @@ -1,0 +2,12 @@ +Thu Apr 16 07:39:02 UTC 2020 - Tomáš Chvátal <tchva...@suse.com> + +- Update to 1.22.1: + * Fix quoted-strings rule with only-when-needed on corner cases + * Add check-keys option to the truthy rule + * Fix quoted-strings rule not working on sequences items + * Sunset Python 2 + * Fix new-lines rule on Python 3 with DOS line endings + * Fix quoted-strings rule not working for string values matching scalars + * Add required: only-when-needed option to the quoted-strings rule + +------------------------------------------------------------------- Old: ---- yamllint-1.20.0.tar.gz New: ---- yamllint-1.22.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-yamllint.spec ++++++ --- /var/tmp/diff_new_pack.sRjDLH/_old 2020-04-20 12:55:50.280761372 +0200 +++ /var/tmp/diff_new_pack.sRjDLH/_new 2020-04-20 12:55:50.284761378 +0200 @@ -18,7 +18,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-yamllint -Version: 1.20.0 +Version: 1.22.1 Release: 0 Summary: A linter for YAML files License: GPL-3.0-only @@ -30,6 +30,8 @@ BuildRequires: python-rpm-macros Requires: python-PyYAML Requires: python-pathspec >= 0.5.3 +Requires(post): update-alternatives +Requires(postun): update-alternatives BuildArch: noarch # SECTION test requirements BuildRequires: %{python_module PyYAML} @@ -53,14 +55,22 @@ %install %python_install %python_expand %fdupes %{buildroot}%{$python_sitelib} +%python_clone -a %{buildroot}%{_bindir}/yamllint %check -%python_exec -m unittest discover +export LANG="en_US.UTF8" +%python_exec -m unittest discover -v + +%post +%python_install_alternative yamllint + +%postun +%python_uninstall_alternative yamllint %files %{python_files} %doc README.rst %license LICENSE -%python3_only %{_bindir}/yamllint +%python_alternative %{_bindir}/yamllint %{python_sitelib}/* %changelog ++++++ yamllint-1.20.0.tar.gz -> yamllint-1.22.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yamllint-1.20.0/PKG-INFO new/yamllint-1.22.1/PKG-INFO --- old/yamllint-1.20.0/PKG-INFO 2019-12-26 16:07:34.000000000 +0100 +++ new/yamllint-1.22.1/PKG-INFO 2020-04-15 07:57:48.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.2 Name: yamllint -Version: 1.20.0 +Version: 1.22.1 Summary: A linter for YAML files. Home-page: https://github.com/adrienverge/yamllint Author: Adrien Vergé diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yamllint-1.20.0/README.rst new/yamllint-1.22.1/README.rst --- old/yamllint-1.20.0/README.rst 2019-07-07 18:14:22.000000000 +0200 +++ new/yamllint-1.22.1/README.rst 2020-04-10 16:31:09.000000000 +0200 @@ -21,6 +21,10 @@ Written in Python (compatible with Python 2 & 3). +⚠ Python 2 upstream support stopped on January 1, 2020. yamllint will keep +best-effort support for Python 2.7 until January 1, 2021. Passed that date, +yamllint will drop all Python 2-related code. + Documentation ------------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yamllint-1.20.0/tests/rules/test_quoted_strings.py new/yamllint-1.22.1/tests/rules/test_quoted_strings.py --- old/yamllint-1.20.0/tests/rules/test_quoted_strings.py 2018-10-17 10:18:32.000000000 +0200 +++ new/yamllint-1.22.1/tests/rules/test_quoted_strings.py 2020-04-15 07:54:25.000000000 +0200 @@ -22,6 +22,7 @@ def test_disabled(self): conf = 'quoted-strings: disable' + self.check('---\n' 'foo: bar\n', conf) self.check('---\n' @@ -30,23 +31,34 @@ 'foo: \'bar\'\n', conf) self.check('---\n' 'bar: 123\n', conf) + self.check('---\n' + 'bar: "123"\n', conf) def test_quote_type_any(self): conf = 'quoted-strings: {quote-type: any}\n' + self.check('---\n' 'boolean1: true\n' 'number1: 123\n' 'string1: foo\n' # fails 'string2: "foo"\n' - 'string3: \'bar\'\n' - 'string4: !!str genericstring\n' - 'string5: !!str 456\n' - 'string6: !!str "quotedgenericstring"\n' + 'string3: "true"\n' + 'string4: "123"\n' + 'string5: \'bar\'\n' + 'string6: !!str genericstring\n' + 'string7: !!str 456\n' + 'string8: !!str "quotedgenericstring"\n' 'binary: !!binary binstring\n' 'integer: !!int intstring\n' 'boolean2: !!bool boolstring\n' - 'boolean3: !!bool "quotedboolstring"\n', - conf, problem=(4, 10)) + 'boolean3: !!bool "quotedboolstring"\n' + 'block-seq:\n' + ' - foo\n' # fails + ' - "foo"\n' + 'flow-seq: [foo, "foo"]\n' # fails + 'flow-map: {a: foo, b: "foo"}\n', # fails + conf, problem1=(4, 10), problem2=(17, 5), + problem3=(19, 12), problem4=(20, 15)) self.check('---\n' 'multiline string 1: |\n' ' line 1\n' @@ -55,7 +67,7 @@ ' word 1\n' ' word 2\n' 'multiline string 3:\n' - ' word 1\n' + ' word 1\n' # fails ' word 2\n' 'multiline string 4:\n' ' "word 1\\\n' @@ -64,20 +76,31 @@ def test_quote_type_single(self): conf = 'quoted-strings: {quote-type: single}\n' + self.check('---\n' 'boolean1: true\n' 'number1: 123\n' 'string1: foo\n' # fails 'string2: "foo"\n' # fails - 'string3: \'bar\'\n' - 'string4: !!str genericstring\n' - 'string5: !!str 456\n' - 'string6: !!str "quotedgenericstring"\n' + 'string3: "true"\n' # fails + 'string4: "123"\n' # fails + 'string5: \'bar\'\n' + 'string6: !!str genericstring\n' + 'string7: !!str 456\n' + 'string8: !!str "quotedgenericstring"\n' 'binary: !!binary binstring\n' 'integer: !!int intstring\n' 'boolean2: !!bool boolstring\n' - 'boolean3: !!bool "quotedboolstring"\n', - conf, problem1=(4, 10), problem2=(5, 10)) + 'boolean3: !!bool "quotedboolstring"\n' + 'block-seq:\n' + ' - foo\n' # fails + ' - "foo"\n' # fails + 'flow-seq: [foo, "foo"]\n' # fails + 'flow-map: {a: foo, b: "foo"}\n', # fails + conf, problem1=(4, 10), problem2=(5, 10), problem3=(6, 10), + problem4=(7, 10), problem5=(17, 5), problem6=(18, 5), + problem7=(19, 12), problem8=(19, 17), problem9=(20, 15), + problem10=(20, 23)) self.check('---\n' 'multiline string 1: |\n' ' line 1\n' @@ -86,7 +109,7 @@ ' word 1\n' ' word 2\n' 'multiline string 3:\n' - ' word 1\n' + ' word 1\n' # fails ' word 2\n' 'multiline string 4:\n' ' "word 1\\\n' @@ -95,20 +118,29 @@ def test_quote_type_double(self): conf = 'quoted-strings: {quote-type: double}\n' + self.check('---\n' 'boolean1: true\n' 'number1: 123\n' 'string1: foo\n' # fails 'string2: "foo"\n' - 'string3: \'bar\'\n' # fails - 'string4: !!str genericstring\n' - 'string5: !!str 456\n' - 'string6: !!str "quotedgenericstring"\n' + 'string3: "true"\n' + 'string4: "123"\n' + 'string5: \'bar\'\n' # fails + 'string6: !!str genericstring\n' + 'string7: !!str 456\n' + 'string8: !!str "quotedgenericstring"\n' 'binary: !!binary binstring\n' 'integer: !!int intstring\n' 'boolean2: !!bool boolstring\n' - 'boolean3: !!bool "quotedboolstring"\n', - conf, problem1=(4, 10), problem2=(6, 10)) + 'boolean3: !!bool "quotedboolstring"\n' + 'block-seq:\n' + ' - foo\n' # fails + ' - "foo"\n' + 'flow-seq: [foo, "foo"]\n' # fails + 'flow-map: {a: foo, b: "foo"}\n', # fails + conf, problem1=(4, 10), problem2=(8, 10), problem3=(17, 5), + problem4=(19, 12), problem5=(20, 15)) self.check('---\n' 'multiline string 1: |\n' ' line 1\n' @@ -117,9 +149,211 @@ ' word 1\n' ' word 2\n' 'multiline string 3:\n' - ' word 1\n' + ' word 1\n' # fails ' word 2\n' 'multiline string 4:\n' ' "word 1\\\n' ' word 2"\n', conf, problem1=(9, 3)) + + def test_any_quotes_not_required(self): + conf = 'quoted-strings: {quote-type: any, required: false}\n' + + self.check('---\n' + 'boolean1: true\n' + 'number1: 123\n' + 'string1: foo\n' + 'string2: "foo"\n' + 'string3: "true"\n' + 'string4: "123"\n' + 'string5: \'bar\'\n' + 'string6: !!str genericstring\n' + 'string7: !!str 456\n' + 'string8: !!str "quotedgenericstring"\n' + 'binary: !!binary binstring\n' + 'integer: !!int intstring\n' + 'boolean2: !!bool boolstring\n' + 'boolean3: !!bool "quotedboolstring"\n' + 'block-seq:\n' + ' - foo\n' # fails + ' - "foo"\n' + 'flow-seq: [foo, "foo"]\n' # fails + 'flow-map: {a: foo, b: "foo"}\n', # fails + conf) + self.check('---\n' + 'multiline string 1: |\n' + ' line 1\n' + ' line 2\n' + 'multiline string 2: >\n' + ' word 1\n' + ' word 2\n' + 'multiline string 3:\n' + ' word 1\n' + ' word 2\n' + 'multiline string 4:\n' + ' "word 1\\\n' + ' word 2"\n', + conf) + + def test_single_quotes_not_required(self): + conf = 'quoted-strings: {quote-type: single, required: false}\n' + + self.check('---\n' + 'boolean1: true\n' + 'number1: 123\n' + 'string1: foo\n' + 'string2: "foo"\n' # fails + 'string3: "true"\n' # fails + 'string4: "123"\n' # fails + 'string5: \'bar\'\n' + 'string6: !!str genericstring\n' + 'string7: !!str 456\n' + 'string8: !!str "quotedgenericstring"\n' + 'binary: !!binary binstring\n' + 'integer: !!int intstring\n' + 'boolean2: !!bool boolstring\n' + 'boolean3: !!bool "quotedboolstring"\n' + 'block-seq:\n' + ' - foo\n' # fails + ' - "foo"\n' + 'flow-seq: [foo, "foo"]\n' # fails + 'flow-map: {a: foo, b: "foo"}\n', # fails + conf, problem1=(5, 10), problem2=(6, 10), problem3=(7, 10), + problem4=(18, 5), problem5=(19, 17), problem6=(20, 23)) + self.check('---\n' + 'multiline string 1: |\n' + ' line 1\n' + ' line 2\n' + 'multiline string 2: >\n' + ' word 1\n' + ' word 2\n' + 'multiline string 3:\n' + ' word 1\n' + ' word 2\n' + 'multiline string 4:\n' + ' "word 1\\\n' # fails + ' word 2"\n', + conf, problem1=(12, 3)) + + def test_only_when_needed(self): + conf = 'quoted-strings: {required: only-when-needed}\n' + + self.check('---\n' + 'boolean1: true\n' + 'number1: 123\n' + 'string1: foo\n' + 'string2: "foo"\n' # fails + 'string3: "true"\n' + 'string4: "123"\n' + 'string5: \'bar\'\n' # fails + 'string6: !!str genericstring\n' + 'string7: !!str 456\n' + 'string8: !!str "quotedgenericstring"\n' + 'binary: !!binary binstring\n' + 'integer: !!int intstring\n' + 'boolean2: !!bool boolstring\n' + 'boolean3: !!bool "quotedboolstring"\n' + 'block-seq:\n' + ' - foo\n' + ' - "foo"\n' # fails + 'flow-seq: [foo, "foo"]\n' # fails + 'flow-map: {a: foo, b: "foo"}\n', # fails + conf, problem1=(5, 10), problem2=(8, 10), problem3=(18, 5), + problem4=(19, 17), problem5=(20, 23)) + self.check('---\n' + 'multiline string 1: |\n' + ' line 1\n' + ' line 2\n' + 'multiline string 2: >\n' + ' word 1\n' + ' word 2\n' + 'multiline string 3:\n' + ' word 1\n' + ' word 2\n' + 'multiline string 4:\n' + ' "word 1\\\n' # fails + ' word 2"\n', + conf, problem1=(12, 3)) + + def test_only_when_needed_single_quotes(self): + conf = ('quoted-strings: {quote-type: single,\n' + ' required: only-when-needed}\n') + + self.check('---\n' + 'boolean1: true\n' + 'number1: 123\n' + 'string1: foo\n' + 'string2: "foo"\n' # fails + 'string3: "true"\n' # fails + 'string4: "123"\n' # fails + 'string5: \'bar\'\n' # fails + 'string6: !!str genericstring\n' + 'string7: !!str 456\n' + 'string8: !!str "quotedgenericstring"\n' + 'binary: !!binary binstring\n' + 'integer: !!int intstring\n' + 'boolean2: !!bool boolstring\n' + 'boolean3: !!bool "quotedboolstring"\n' + 'block-seq:\n' + ' - foo\n' + ' - "foo"\n' # fails + 'flow-seq: [foo, "foo"]\n' # fails + 'flow-map: {a: foo, b: "foo"}\n', # fails + conf, problem1=(5, 10), problem2=(6, 10), problem3=(7, 10), + problem4=(8, 10), problem5=(18, 5), problem6=(19, 17), + problem7=(20, 23)) + self.check('---\n' + 'multiline string 1: |\n' + ' line 1\n' + ' line 2\n' + 'multiline string 2: >\n' + ' word 1\n' + ' word 2\n' + 'multiline string 3:\n' + ' word 1\n' + ' word 2\n' + 'multiline string 4:\n' + ' "word 1\\\n' # fails + ' word 2"\n', + conf, problem1=(12, 3)) + + def test_only_when_needed_corner_cases(self): + conf = 'quoted-strings: {required: only-when-needed}\n' + + self.check('---\n' + '- ""\n' + '- "- item"\n' + '- "key: value"\n' + '- "%H:%M:%S"\n' + '- "%wheel ALL=(ALL) NOPASSWD: ALL"\n' + '- \'"quoted"\'\n' + '- "\'foo\' == \'bar\'"\n' + '- "\'Mac\' in ansible_facts.product_name"\n', + conf) + self.check('---\n' + 'k1: ""\n' + 'k2: "- item"\n' + 'k3: "key: value"\n' + 'k4: "%H:%M:%S"\n' + 'k5: "%wheel ALL=(ALL) NOPASSWD: ALL"\n' + 'k6: \'"quoted"\'\n' + 'k7: "\'foo\' == \'bar\'"\n' + 'k8: "\'Mac\' in ansible_facts.product_name"\n', + conf) + + self.check('---\n' + '- ---\n' + '- "---"\n' # fails + '- ----------\n' + '- "----------"\n' # fails + '- :wq\n' + '- ":wq"\n', # fails + conf, problem1=(3, 3), problem2=(5, 3), problem3=(7, 3)) + self.check('---\n' + 'k1: ---\n' + 'k2: "---"\n' # fails + 'k3: ----------\n' + 'k4: "----------"\n' # fails + 'k5: :wq\n' + 'k6: ":wq"\n', # fails + conf, problem1=(3, 5), problem2=(5, 5), problem3=(7, 5)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yamllint-1.20.0/tests/rules/test_truthy.py new/yamllint-1.22.1/tests/rules/test_truthy.py --- old/yamllint-1.20.0/tests/rules/test_truthy.py 2019-06-07 09:59:31.000000000 +0200 +++ new/yamllint-1.22.1/tests/rules/test_truthy.py 2020-04-08 12:31:08.000000000 +0200 @@ -114,3 +114,33 @@ 'boolean5: !!bool off\n' 'boolean6: !!bool NO\n', conf) + + def test_check_keys_disabled(self): + conf = ('truthy:\n' + ' allowed-values: []\n' + ' check-keys: false\n' + 'key-duplicates: disable\n') + self.check('---\n' + 'YES: 0\n' + 'Yes: 0\n' + 'yes: 0\n' + 'No: 0\n' + 'No: 0\n' + 'no: 0\n' + 'TRUE: 0\n' + 'True: 0\n' + 'true: 0\n' + 'FALSE: 0\n' + 'False: 0\n' + 'false: 0\n' + 'ON: 0\n' + 'On: 0\n' + 'on: 0\n' + 'OFF: 0\n' + 'Off: 0\n' + 'off: 0\n' + 'YES:\n' + ' Yes:\n' + ' yes:\n' + ' on: 0\n', + conf) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yamllint-1.20.0/tests/test_cli.py new/yamllint-1.22.1/tests/test_cli.py --- old/yamllint-1.20.0/tests/test_cli.py 2019-12-26 16:04:47.000000000 +0100 +++ new/yamllint-1.22.1/tests/test_cli.py 2020-03-31 12:32:06.000000000 +0200 @@ -32,6 +32,29 @@ from yamllint import config +class RunContext(object): + """Context manager for ``cli.run()`` to capture exit code and streams.""" + + def __init__(self, case): + self.stdout = self.stderr = None + self._raises_ctx = case.assertRaises(SystemExit) + + def __enter__(self): + self._raises_ctx.__enter__() + sys.stdout = self.outstream = StringIO() + sys.stderr = self.errstream = StringIO() + return self + + def __exit__(self, *exc_info): + self.stdout, sys.stdout = self.outstream.getvalue(), sys.__stdout__ + self.stderr, sys.stderr = self.errstream.getvalue(), sys.__stderr__ + return self._raises_ctx.__exit__(*exc_info) + + @property + def returncode(self): + return self._raises_ctx.exception.code + + class CommandLineTestCase(unittest.TestCase): @classmethod def setUpClass(cls): @@ -59,12 +82,15 @@ 'no-yaml.json': '---\n' 'key: value\n', # non-ASCII chars - 'non-ascii/utf-8': ( + 'non-ascii/éçäγλνπ¥/utf-8': ( u'---\n' u'- hétérogénéité\n' u'# 19.99 €\n' u'- お早う御座います。\n' u'# الأَبْجَدِيَّة العَرَبِيَّة\n').encode('utf-8'), + # dos line endings yaml + 'dos.yml': '---\r\n' + 'dos: true', }) @classmethod @@ -78,6 +104,7 @@ self.assertEqual( sorted(cli.find_files_recursively([self.wd], conf)), [os.path.join(self.wd, 'a.yaml'), + os.path.join(self.wd, 'dos.yml'), os.path.join(self.wd, 'empty.yml'), os.path.join(self.wd, 's/s/s/s/s/s/s/s/s/s/s/s/s/s/s/file.yaml'), os.path.join(self.wd, 'sub/ok.yaml'), @@ -123,7 +150,8 @@ ' - \'*.yml\'\n') self.assertEqual( sorted(cli.find_files_recursively([self.wd], conf)), - [os.path.join(self.wd, 'empty.yml')] + [os.path.join(self.wd, 'dos.yml'), + os.path.join(self.wd, 'empty.yml')] ) conf = config.YamlLintConfig('extends: default\n' @@ -140,9 +168,10 @@ self.assertEqual( sorted(cli.find_files_recursively([self.wd], conf)), [os.path.join(self.wd, 'a.yaml'), + os.path.join(self.wd, 'dos.yml'), os.path.join(self.wd, 'empty.yml'), os.path.join(self.wd, 'no-yaml.json'), - os.path.join(self.wd, 'non-ascii/utf-8'), + os.path.join(self.wd, 'non-ascii/éçäγλνπ¥/utf-8'), os.path.join(self.wd, 's/s/s/s/s/s/s/s/s/s/s/s/s/s/s/file.yaml'), os.path.join(self.wd, 'sub/ok.yaml'), os.path.join(self.wd, 'warn.yaml')] @@ -156,9 +185,10 @@ self.assertEqual( sorted(cli.find_files_recursively([self.wd], conf)), [os.path.join(self.wd, 'a.yaml'), + os.path.join(self.wd, 'dos.yml'), os.path.join(self.wd, 'empty.yml'), os.path.join(self.wd, 'no-yaml.json'), - os.path.join(self.wd, 'non-ascii/utf-8'), + os.path.join(self.wd, 'non-ascii/éçäγλνπ¥/utf-8'), os.path.join(self.wd, 's/s/s/s/s/s/s/s/s/s/s/s/s/s/s/file.yaml'), os.path.join(self.wd, 'sub/ok.yaml'), os.path.join(self.wd, 'warn.yaml')] @@ -170,205 +200,148 @@ ' - \'**/utf-8\'\n') self.assertEqual( sorted(cli.find_files_recursively([self.wd], conf)), - [os.path.join(self.wd, 'non-ascii/utf-8')] + [os.path.join(self.wd, 'non-ascii/éçäγλνπ¥/utf-8')] ) def test_run_with_bad_arguments(self): - sys.stdout, sys.stderr = StringIO(), StringIO() - with self.assertRaises(SystemExit) as ctx: + with RunContext(self) as ctx: cli.run(()) + self.assertNotEqual(ctx.returncode, 0) + self.assertEqual(ctx.stdout, '') + self.assertRegexpMatches(ctx.stderr, r'^usage') - self.assertNotEqual(ctx.exception.code, 0) - - out, err = sys.stdout.getvalue(), sys.stderr.getvalue() - self.assertEqual(out, '') - self.assertRegexpMatches(err, r'^usage') - - sys.stdout, sys.stderr = StringIO(), StringIO() - with self.assertRaises(SystemExit) as ctx: + with RunContext(self) as ctx: cli.run(('--unknown-arg', )) + self.assertNotEqual(ctx.returncode, 0) + self.assertEqual(ctx.stdout, '') + self.assertRegexpMatches(ctx.stderr, r'^usage') - self.assertNotEqual(ctx.exception.code, 0) - - out, err = sys.stdout.getvalue(), sys.stderr.getvalue() - self.assertEqual(out, '') - self.assertRegexpMatches(err, r'^usage') - - sys.stdout, sys.stderr = StringIO(), StringIO() - with self.assertRaises(SystemExit) as ctx: + with RunContext(self) as ctx: cli.run(('-c', './conf.yaml', '-d', 'relaxed', 'file')) - - self.assertNotEqual(ctx.exception.code, 0) - - out, err = sys.stdout.getvalue(), sys.stderr.getvalue() - self.assertEqual(out, '') + self.assertNotEqual(ctx.returncode, 0) + self.assertEqual(ctx.stdout, '') self.assertRegexpMatches( - err.splitlines()[-1], + ctx.stderr.splitlines()[-1], r'^yamllint: error: argument -d\/--config-data: ' r'not allowed with argument -c\/--config-file$' ) # checks if reading from stdin and files are mutually exclusive - sys.stdout, sys.stderr = StringIO(), StringIO() - with self.assertRaises(SystemExit) as ctx: + with RunContext(self) as ctx: cli.run(('-', 'file')) - - self.assertNotEqual(ctx.exception.code, 0) - - out, err = sys.stdout.getvalue(), sys.stderr.getvalue() - self.assertEqual(out, '') - self.assertRegexpMatches(err, r'^usage') + self.assertNotEqual(ctx.returncode, 0) + self.assertEqual(ctx.stdout, '') + self.assertRegexpMatches(ctx.stderr, r'^usage') def test_run_with_bad_config(self): - sys.stdout, sys.stderr = StringIO(), StringIO() - with self.assertRaises(SystemExit) as ctx: + with RunContext(self) as ctx: cli.run(('-d', 'rules: {a: b}', 'file')) - - self.assertEqual(ctx.exception.code, -1) - - out, err = sys.stdout.getvalue(), sys.stderr.getvalue() - self.assertEqual(out, '') - self.assertRegexpMatches(err, r'^invalid config: no such rule') + self.assertEqual(ctx.returncode, -1) + self.assertEqual(ctx.stdout, '') + self.assertRegexpMatches(ctx.stderr, r'^invalid config: no such rule') def test_run_with_empty_config(self): - sys.stdout, sys.stderr = StringIO(), StringIO() - with self.assertRaises(SystemExit) as ctx: + with RunContext(self) as ctx: cli.run(('-d', '', 'file')) - - self.assertEqual(ctx.exception.code, -1) - - out, err = sys.stdout.getvalue(), sys.stderr.getvalue() - self.assertEqual(out, '') - self.assertRegexpMatches(err, r'^invalid config: not a dict') + self.assertEqual(ctx.returncode, -1) + self.assertEqual(ctx.stdout, '') + self.assertRegexpMatches(ctx.stderr, r'^invalid config: not a dict') def test_run_with_config_file(self): with open(os.path.join(self.wd, 'config'), 'w') as f: f.write('rules: {trailing-spaces: disable}') - with self.assertRaises(SystemExit) as ctx: + with RunContext(self) as ctx: cli.run(('-c', f.name, os.path.join(self.wd, 'a.yaml'))) - self.assertEqual(ctx.exception.code, 0) + self.assertEqual(ctx.returncode, 0) with open(os.path.join(self.wd, 'config'), 'w') as f: f.write('rules: {trailing-spaces: enable}') - with self.assertRaises(SystemExit) as ctx: + with RunContext(self) as ctx: cli.run(('-c', f.name, os.path.join(self.wd, 'a.yaml'))) - self.assertEqual(ctx.exception.code, 1) + self.assertEqual(ctx.returncode, 1) def test_run_with_user_global_config_file(self): home = os.path.join(self.wd, 'fake-home') - os.mkdir(home) - dir = os.path.join(home, '.config') - os.mkdir(dir) - dir = os.path.join(dir, 'yamllint') - os.mkdir(dir) + dir = os.path.join(home, '.config', 'yamllint') + os.makedirs(dir) config = os.path.join(dir, 'config') - temp = os.environ['HOME'] + self.addCleanup(os.environ.update, HOME=os.environ['HOME']) os.environ['HOME'] = home with open(config, 'w') as f: f.write('rules: {trailing-spaces: disable}') - with self.assertRaises(SystemExit) as ctx: + with RunContext(self) as ctx: cli.run((os.path.join(self.wd, 'a.yaml'), )) - self.assertEqual(ctx.exception.code, 0) + self.assertEqual(ctx.returncode, 0) with open(config, 'w') as f: f.write('rules: {trailing-spaces: enable}') - with self.assertRaises(SystemExit) as ctx: + with RunContext(self) as ctx: cli.run((os.path.join(self.wd, 'a.yaml'), )) - self.assertEqual(ctx.exception.code, 1) - - os.environ['HOME'] = temp + self.assertEqual(ctx.returncode, 1) def test_run_version(self): - sys.stdout, sys.stderr = StringIO(), StringIO() - with self.assertRaises(SystemExit) as ctx: + with RunContext(self) as ctx: cli.run(('--version', )) - - self.assertEqual(ctx.exception.code, 0) - - out, err = sys.stdout.getvalue(), sys.stderr.getvalue() - self.assertRegexpMatches(out + err, r'yamllint \d+\.\d+') + self.assertEqual(ctx.returncode, 0) + self.assertRegexpMatches(ctx.stdout + ctx.stderr, r'yamllint \d+\.\d+') def test_run_non_existing_file(self): - file = os.path.join(self.wd, 'i-do-not-exist.yaml') + path = os.path.join(self.wd, 'i-do-not-exist.yaml') - sys.stdout, sys.stderr = StringIO(), StringIO() - with self.assertRaises(SystemExit) as ctx: - cli.run(('-f', 'parsable', file)) - - self.assertEqual(ctx.exception.code, -1) - - out, err = sys.stdout.getvalue(), sys.stderr.getvalue() - self.assertEqual(out, '') - self.assertRegexpMatches(err, r'No such file or directory') + with RunContext(self) as ctx: + cli.run(('-f', 'parsable', path)) + self.assertEqual(ctx.returncode, -1) + self.assertEqual(ctx.stdout, '') + self.assertRegexpMatches(ctx.stderr, r'No such file or directory') def test_run_one_problem_file(self): - file = os.path.join(self.wd, 'a.yaml') - - sys.stdout, sys.stderr = StringIO(), StringIO() - with self.assertRaises(SystemExit) as ctx: - cli.run(('-f', 'parsable', file)) - - self.assertEqual(ctx.exception.code, 1) + path = os.path.join(self.wd, 'a.yaml') - out, err = sys.stdout.getvalue(), sys.stderr.getvalue() - self.assertEqual(out, ( + with RunContext(self) as ctx: + cli.run(('-f', 'parsable', path)) + self.assertEqual(ctx.returncode, 1) + self.assertEqual(ctx.stdout, ( '%s:2:4: [error] trailing spaces (trailing-spaces)\n' '%s:3:4: [error] no new line character at the end of file ' - '(new-line-at-end-of-file)\n') % (file, file)) - self.assertEqual(err, '') + '(new-line-at-end-of-file)\n' % (path, path))) + self.assertEqual(ctx.stderr, '') def test_run_one_warning(self): - file = os.path.join(self.wd, 'warn.yaml') - - sys.stdout, sys.stderr = StringIO(), StringIO() - with self.assertRaises(SystemExit) as ctx: - cli.run(('-f', 'parsable', file)) + path = os.path.join(self.wd, 'warn.yaml') - self.assertEqual(ctx.exception.code, 0) + with RunContext(self) as ctx: + cli.run(('-f', 'parsable', path)) + self.assertEqual(ctx.returncode, 0) def test_run_warning_in_strict_mode(self): - file = os.path.join(self.wd, 'warn.yaml') - - sys.stdout, sys.stderr = StringIO(), StringIO() - with self.assertRaises(SystemExit) as ctx: - cli.run(('-f', 'parsable', '--strict', file)) + path = os.path.join(self.wd, 'warn.yaml') - self.assertEqual(ctx.exception.code, 2) + with RunContext(self) as ctx: + cli.run(('-f', 'parsable', '--strict', path)) + self.assertEqual(ctx.returncode, 2) def test_run_one_ok_file(self): - file = os.path.join(self.wd, 'sub', 'ok.yaml') + path = os.path.join(self.wd, 'sub', 'ok.yaml') - sys.stdout, sys.stderr = StringIO(), StringIO() - with self.assertRaises(SystemExit) as ctx: - cli.run(('-f', 'parsable', file)) - - self.assertEqual(ctx.exception.code, 0) - - out, err = sys.stdout.getvalue(), sys.stderr.getvalue() - self.assertEqual(out, '') - self.assertEqual(err, '') + with RunContext(self) as ctx: + cli.run(('-f', 'parsable', path)) + self.assertEqual((ctx.returncode, ctx.stdout, ctx.stderr), (0, '', '')) def test_run_empty_file(self): - file = os.path.join(self.wd, 'empty.yml') - - sys.stdout, sys.stderr = StringIO(), StringIO() - with self.assertRaises(SystemExit) as ctx: - cli.run(('-f', 'parsable', file)) - - self.assertEqual(ctx.exception.code, 0) + path = os.path.join(self.wd, 'empty.yml') - out, err = sys.stdout.getvalue(), sys.stderr.getvalue() - self.assertEqual(out, '') - self.assertEqual(err, '') + with RunContext(self) as ctx: + cli.run(('-f', 'parsable', path)) + self.assertEqual((ctx.returncode, ctx.stdout, ctx.stderr), (0, '', '')) def test_run_non_ascii_file(self): - file = os.path.join(self.wd, 'non-ascii', 'utf-8') + path = os.path.join(self.wd, 'non-ascii', 'éçäγλνπ¥', 'utf-8') # Make sure the default localization conditions on this "system" # support UTF-8 encoding. @@ -377,63 +350,46 @@ locale.setlocale(locale.LC_ALL, 'C.UTF-8') except locale.Error: locale.setlocale(locale.LC_ALL, 'en_US.UTF-8') + self.addCleanup(locale.setlocale, locale.LC_ALL, loc) - sys.stdout, sys.stderr = StringIO(), StringIO() - with self.assertRaises(SystemExit) as ctx: - cli.run(('-f', 'parsable', file)) - - locale.setlocale(locale.LC_ALL, loc) - - self.assertEqual(ctx.exception.code, 0) - - out, err = sys.stdout.getvalue(), sys.stderr.getvalue() - self.assertEqual(out, '') - self.assertEqual(err, '') + with RunContext(self) as ctx: + cli.run(('-f', 'parsable', path)) + self.assertEqual((ctx.returncode, ctx.stdout, ctx.stderr), (0, '', '')) def test_run_multiple_files(self): items = [os.path.join(self.wd, 'empty.yml'), os.path.join(self.wd, 's')] - file = items[1] + '/s/s/s/s/s/s/s/s/s/s/s/s/s/s/file.yaml' + path = items[1] + '/s/s/s/s/s/s/s/s/s/s/s/s/s/s/file.yaml' - sys.stdout, sys.stderr = StringIO(), StringIO() - with self.assertRaises(SystemExit) as ctx: + with RunContext(self) as ctx: cli.run(['-f', 'parsable'] + items) - - self.assertEqual(ctx.exception.code, 1) - - out, err = sys.stdout.getvalue(), sys.stderr.getvalue() - self.assertEqual(out, ( + self.assertEqual((ctx.returncode, ctx.stderr), (1, '')) + self.assertEqual(ctx.stdout, ( '%s:3:1: [error] duplication of key "key" in mapping ' - '(key-duplicates)\n') % file) - self.assertEqual(err, '') + '(key-duplicates)\n') % path) def test_run_piped_output_nocolor(self): - file = os.path.join(self.wd, 'a.yaml') - - sys.stdout, sys.stderr = StringIO(), StringIO() - with self.assertRaises(SystemExit) as ctx: - cli.run((file, )) - - self.assertEqual(ctx.exception.code, 1) + path = os.path.join(self.wd, 'a.yaml') - out, err = sys.stdout.getvalue(), sys.stderr.getvalue() - self.assertEqual(out, ( + with RunContext(self) as ctx: + cli.run((path, )) + self.assertEqual((ctx.returncode, ctx.stderr), (1, '')) + self.assertEqual(ctx.stdout, ( '%s\n' ' 2:4 error trailing spaces (trailing-spaces)\n' ' 3:4 error no new line character at the end of file ' '(new-line-at-end-of-file)\n' - '\n' % file)) - self.assertEqual(err, '') + '\n' % path)) def test_run_default_format_output_in_tty(self): - file = os.path.join(self.wd, 'a.yaml') + path = os.path.join(self.wd, 'a.yaml') # Create a pseudo-TTY and redirect stdout to it master, slave = pty.openpty() sys.stdout = sys.stderr = os.fdopen(slave, 'w') with self.assertRaises(SystemExit) as ctx: - cli.run((file, )) + cli.run((path, )) sys.stdout.flush() self.assertEqual(ctx.exception.code, 1) @@ -456,114 +412,108 @@ ' \033[2m3:4\033[0m \033[31merror\033[0m ' 'no new line character at the end of file ' '\033[2m(new-line-at-end-of-file)\033[0m\n' - '\n' % file)) + '\n' % path)) def test_run_default_format_output_without_tty(self): - file = os.path.join(self.wd, 'a.yaml') - - sys.stdout, sys.stderr = StringIO(), StringIO() - with self.assertRaises(SystemExit) as ctx: - cli.run((file, )) + path = os.path.join(self.wd, 'a.yaml') - self.assertEqual(ctx.exception.code, 1) - - out, err = sys.stdout.getvalue(), sys.stderr.getvalue() - self.assertEqual(out, ( + with RunContext(self) as ctx: + cli.run((path, )) + expected_out = ( '%s\n' ' 2:4 error trailing spaces (trailing-spaces)\n' ' 3:4 error no new line character at the end of file ' '(new-line-at-end-of-file)\n' - '\n' % file)) - self.assertEqual(err, '') + '\n' % path) + self.assertEqual( + (ctx.returncode, ctx.stdout, ctx.stderr), (1, expected_out, '')) def test_run_auto_output_without_tty_output(self): - file = os.path.join(self.wd, 'a.yaml') - - sys.stdout, sys.stderr = StringIO(), StringIO() - with self.assertRaises(SystemExit) as ctx: - cli.run((file, '--format', 'auto')) + path = os.path.join(self.wd, 'a.yaml') - self.assertEqual(ctx.exception.code, 1) - - out, err = sys.stdout.getvalue(), sys.stderr.getvalue() - self.assertEqual(out, ( + with RunContext(self) as ctx: + cli.run((path, '--format', 'auto')) + expected_out = ( '%s\n' ' 2:4 error trailing spaces (trailing-spaces)\n' ' 3:4 error no new line character at the end of file ' '(new-line-at-end-of-file)\n' - '\n' % file)) - self.assertEqual(err, '') + '\n' % path) + self.assertEqual( + (ctx.returncode, ctx.stdout, ctx.stderr), (1, expected_out, '')) def test_run_format_colored(self): - file = os.path.join(self.wd, 'a.yaml') - - sys.stdout, sys.stderr = StringIO(), StringIO() - with self.assertRaises(SystemExit) as ctx: - cli.run((file, '--format', 'colored')) + path = os.path.join(self.wd, 'a.yaml') - self.assertEqual(ctx.exception.code, 1) - - out, err = sys.stdout.getvalue(), sys.stderr.getvalue() - self.assertEqual(out, ( + with RunContext(self) as ctx: + cli.run((path, '--format', 'colored')) + expected_out = ( '\033[4m%s\033[0m\n' ' \033[2m2:4\033[0m \033[31merror\033[0m ' 'trailing spaces \033[2m(trailing-spaces)\033[0m\n' ' \033[2m3:4\033[0m \033[31merror\033[0m ' 'no new line character at the end of file ' '\033[2m(new-line-at-end-of-file)\033[0m\n' - '\n' % file)) - self.assertEqual(err, '') + '\n' % path) + self.assertEqual( + (ctx.returncode, ctx.stdout, ctx.stderr), (1, expected_out, '')) def test_run_read_from_stdin(self): # prepares stdin with an invalid yaml string so that we can check # for its specific error, and be assured that stdin was read - sys.stdout, sys.stderr = StringIO(), StringIO() + self.addCleanup(setattr, sys, 'stdin', sys.__stdin__) sys.stdin = StringIO( 'I am a string\n' 'therefore: I am an error\n') - with self.assertRaises(SystemExit) as ctx: + with RunContext(self) as ctx: cli.run(('-', '-f', 'parsable')) - - self.assertNotEqual(ctx.exception.code, 0) - - out, err = sys.stdout.getvalue(), sys.stderr.getvalue() - self.assertEqual(out, ( + expected_out = ( 'stdin:2:10: [error] syntax error: ' - 'mapping values are not allowed here (syntax)\n')) - self.assertEqual(err, '') + 'mapping values are not allowed here (syntax)\n') + self.assertEqual( + (ctx.returncode, ctx.stdout, ctx.stderr), (1, expected_out, '')) def test_run_no_warnings(self): - file = os.path.join(self.wd, 'a.yaml') + path = os.path.join(self.wd, 'a.yaml') - sys.stdout, sys.stderr = StringIO(), StringIO() - with self.assertRaises(SystemExit) as ctx: - cli.run((file, '--no-warnings', '-f', 'auto')) - - self.assertEqual(ctx.exception.code, 1) - - out, err = sys.stdout.getvalue(), sys.stderr.getvalue() - self.assertEqual(out, ( + with RunContext(self) as ctx: + cli.run((path, '--no-warnings', '-f', 'auto')) + expected_out = ( '%s\n' ' 2:4 error trailing spaces (trailing-spaces)\n' ' 3:4 error no new line character at the end of file ' '(new-line-at-end-of-file)\n' - '\n' % file)) - self.assertEqual(err, '') - - file = os.path.join(self.wd, 'warn.yaml') + '\n' % path) + self.assertEqual( + (ctx.returncode, ctx.stdout, ctx.stderr), (1, expected_out, '')) - sys.stdout, sys.stderr = StringIO(), StringIO() - with self.assertRaises(SystemExit) as ctx: - cli.run((file, '--no-warnings', '-f', 'auto')) + path = os.path.join(self.wd, 'warn.yaml') - self.assertEqual(ctx.exception.code, 0) + with RunContext(self) as ctx: + cli.run((path, '--no-warnings', '-f', 'auto')) + self.assertEqual(ctx.returncode, 0) def test_run_no_warnings_and_strict(self): - file = os.path.join(self.wd, 'warn.yaml') - - sys.stdout, sys.stderr = StringIO(), StringIO() - with self.assertRaises(SystemExit) as ctx: - cli.run((file, '--no-warnings', '-s')) + path = os.path.join(self.wd, 'warn.yaml') - self.assertEqual(ctx.exception.code, 2) + with RunContext(self) as ctx: + cli.run((path, '--no-warnings', '-s')) + self.assertEqual(ctx.returncode, 2) + + def test_run_non_universal_newline(self): + path = os.path.join(self.wd, 'dos.yml') + + with RunContext(self) as ctx: + cli.run(('-d', 'rules:\n new-lines:\n type: dos', path)) + self.assertEqual((ctx.returncode, ctx.stdout, ctx.stderr), (0, '', '')) + + with RunContext(self) as ctx: + cli.run(('-d', 'rules:\n new-lines:\n type: unix', path)) + expected_out = ( + '%s\n' + ' 1:4 error wrong new line character: expected \\n' + ' (new-lines)\n' + '\n' % path) + self.assertEqual( + (ctx.returncode, ctx.stdout, ctx.stderr), (1, expected_out, '')) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yamllint-1.20.0/yamllint/__init__.py new/yamllint-1.22.1/yamllint/__init__.py --- old/yamllint-1.20.0/yamllint/__init__.py 2019-12-26 16:06:16.000000000 +0100 +++ new/yamllint-1.22.1/yamllint/__init__.py 2020-04-15 07:55:42.000000000 +0200 @@ -22,7 +22,7 @@ APP_NAME = 'yamllint' -APP_VERSION = '1.20.0' +APP_VERSION = '1.22.1' APP_DESCRIPTION = __doc__ __author__ = u'Adrien Vergé' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yamllint-1.20.0/yamllint/cli.py new/yamllint-1.22.1/yamllint/cli.py --- old/yamllint-1.20.0/yamllint/cli.py 2019-12-12 09:13:16.000000000 +0100 +++ new/yamllint-1.22.1/yamllint/cli.py 2020-04-08 11:46:13.000000000 +0200 @@ -17,6 +17,7 @@ from __future__ import print_function import argparse +import io import os import platform import sys @@ -176,7 +177,7 @@ for file in find_files_recursively(args.files, conf): filepath = file[2:] if file.startswith('./') else file try: - with open(file) as f: + with io.open(file, newline='') as f: problems = linter.run(f, conf, filepath) except EnvironmentError as e: print(e, file=sys.stderr) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yamllint-1.20.0/yamllint/rules/quoted_strings.py new/yamllint-1.22.1/yamllint/rules/quoted_strings.py --- old/yamllint-1.20.0/yamllint/rules/quoted_strings.py 2019-01-10 10:01:44.000000000 +0100 +++ new/yamllint-1.22.1/yamllint/rules/quoted_strings.py 2020-04-15 07:54:25.000000000 +0200 @@ -15,15 +15,23 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. """ -Use this rule to forbid any string values that are not quoted. -You can also enforce the type of the quote used using the ``quote-type`` option -(``single``, ``double`` or ``any``). +Use this rule to forbid any string values that are not quoted, or to prevent +quoted strings without needing it. You can also enforce the type of the quote +used. + +.. rubric:: Options + +* ``quote-type`` defines allowed quotes: ``single``, ``double`` or ``any`` + (default). +* ``required`` defines whether using quotes in string values is required + (``true``, default) or not (``false``), or only allowed when really needed + (``only-when-needed``). **Note**: Multi-line strings (with ``|`` or ``>``) will not be checked. .. rubric:: Examples -#. With ``quoted-strings: {quote-type: any}`` +#. With ``quoted-strings: {quote-type: any, required: true}`` the following code snippet would **PASS**: :: @@ -37,6 +45,24 @@ :: foo: bar + +#. With ``quoted-strings: {quote-type: single, required: only-when-needed}`` + + the following code snippet would **PASS**: + :: + + foo: bar + bar: foo + not_number: '123' + not_boolean: 'true' + not_comment: '# comment' + not_list: '[1, 2, 3]' + not_map: '{a: 1, b: 2}' + + the following code snippet would **FAIL**: + :: + + foo: 'bar' """ import yaml @@ -45,34 +71,93 @@ ID = 'quoted-strings' TYPE = 'token' -CONF = {'quote-type': ('any', 'single', 'double')} -DEFAULT = {'quote-type': 'any'} +CONF = {'quote-type': ('any', 'single', 'double'), + 'required': (True, False, 'only-when-needed')} +DEFAULT = {'quote-type': 'any', + 'required': True} + +DEFAULT_SCALAR_TAG = u'tag:yaml.org,2002:str' + + +def _quote_match(quote_type, token_style): + return ((quote_type == 'any') or + (quote_type == 'single' and token_style == "'") or + (quote_type == 'double' and token_style == '"')) + + +def _quotes_are_needed(string): + loader = yaml.BaseLoader('key: ' + string) + # Remove the 5 first tokens corresponding to 'key: ' (StreamStartToken, + # BlockMappingStartToken, KeyToken, ScalarToken(value=key), ValueToken) + for _ in range(5): + loader.get_token() + try: + a, b = loader.get_token(), loader.get_token() + if (isinstance(a, yaml.ScalarToken) and a.style is None and + isinstance(b, yaml.BlockEndToken)): + return False + return True + except yaml.scanner.ScannerError: + return True def check(conf, token, prev, next, nextnext, context): + if not (isinstance(token, yaml.tokens.ScalarToken) and + isinstance(prev, (yaml.BlockEntryToken, yaml.FlowEntryToken, + yaml.FlowSequenceStartToken, yaml.TagToken, + yaml.ValueToken))): + + return + + # Ignore explicit types, e.g. !!str testtest or !!int 42 + if (prev and isinstance(prev, yaml.tokens.TagToken) and + prev.value[0] == '!!'): + return + + # Ignore numbers, booleans, etc. + resolver = yaml.resolver.Resolver() + tag = resolver.resolve(yaml.nodes.ScalarNode, token.value, (True, False)) + if token.plain and tag != DEFAULT_SCALAR_TAG: + return + + # Ignore multi-line strings + if (not token.plain) and (token.style == "|" or token.style == ">"): + return + quote_type = conf['quote-type'] + required = conf['required'] - if (isinstance(token, yaml.tokens.ScalarToken) and - isinstance(prev, (yaml.ValueToken, yaml.TagToken))): - # Ignore explicit types, e.g. !!str testtest or !!int 42 - if (prev and isinstance(prev, yaml.tokens.TagToken) and - prev.value[0] == '!!'): - return - - # Ignore numbers, booleans, etc. - resolver = yaml.resolver.Resolver() - if resolver.resolve(yaml.nodes.ScalarNode, token.value, - (True, False)) != 'tag:yaml.org,2002:str': - return - - # Ignore multi-line strings - if (not token.plain) and (token.style == "|" or token.style == ">"): - return - - if ((quote_type == 'single' and token.style != "'") or - (quote_type == 'double' and token.style != '"') or - (quote_type == 'any' and token.style is None)): - yield LintProblem( - token.start_mark.line + 1, - token.start_mark.column + 1, - "string value is not quoted with %s quotes" % (quote_type)) + # Completely relaxed about quotes (same as the rule being disabled) + if required is False and quote_type == 'any': + return + + msg = None + if required is True: + + # Quotes are mandatory and need to match config + if token.style is None or not _quote_match(quote_type, token.style): + msg = "string value is not quoted with %s quotes" % (quote_type) + + elif required is False: + + # Quotes are not mandatory but when used need to match config + if token.style and not _quote_match(quote_type, token.style): + msg = "string value is not quoted with %s quotes" % (quote_type) + + elif not token.plain: + + # Quotes are disallowed when not needed + if (tag == DEFAULT_SCALAR_TAG and token.value and + not _quotes_are_needed(token.value)): + msg = "string value is redundantly quoted with %s quotes" % ( + quote_type) + + # But when used need to match config + elif token.style and not _quote_match(quote_type, token.style): + msg = "string value is not quoted with %s quotes" % (quote_type) + + if msg is not None: + yield LintProblem( + token.start_mark.line + 1, + token.start_mark.column + 1, + msg) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yamllint-1.20.0/yamllint/rules/truthy.py new/yamllint-1.22.1/yamllint/rules/truthy.py --- old/yamllint-1.20.0/yamllint/rules/truthy.py 2019-08-27 09:46:03.000000000 +0200 +++ new/yamllint-1.22.1/yamllint/rules/truthy.py 2020-04-08 12:30:35.000000000 +0200 @@ -30,6 +30,9 @@ ``'False'``, ``'false'``, ``'YES'``, ``'Yes'``, ``'yes'``, ``'NO'``, ``'No'``, ``'no'``, ``'ON'``, ``'On'``, ``'on'``, ``'OFF'``, ``'Off'``, ``'off'``. +* ``check-keys`` disables verification for keys in mappings. By default, + ``truthy`` rule applies to both keys and values. Set this option to ``false`` + to prevent this. .. rubric:: Examples @@ -92,6 +95,22 @@ - false - on - off + +#. With ``truthy: {check-keys: false}`` + + the following code snippet would **PASS**: + :: + + yes: 1 + on: 2 + true: 3 + + the following code snippet would **FAIL**: + :: + + yes: Yes + on: On + true: True """ import yaml @@ -109,14 +128,18 @@ ID = 'truthy' TYPE = 'token' -CONF = {'allowed-values': list(TRUTHY)} -DEFAULT = {'allowed-values': ['true', 'false']} +CONF = {'allowed-values': list(TRUTHY), 'check-keys': bool} +DEFAULT = {'allowed-values': ['true', 'false'], 'check-keys': True} def check(conf, token, prev, next, nextnext, context): if prev and isinstance(prev, yaml.tokens.TagToken): return + if (not conf['check-keys'] and isinstance(prev, yaml.tokens.KeyToken) and + isinstance(token, yaml.tokens.ScalarToken)): + return + if isinstance(token, yaml.tokens.ScalarToken): if (token.value in (set(TRUTHY) - set(conf['allowed-values'])) and token.style is None): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yamllint-1.20.0/yamllint.egg-info/PKG-INFO new/yamllint-1.22.1/yamllint.egg-info/PKG-INFO --- old/yamllint-1.20.0/yamllint.egg-info/PKG-INFO 2019-12-26 16:07:34.000000000 +0100 +++ new/yamllint-1.22.1/yamllint.egg-info/PKG-INFO 2020-04-15 07:57:48.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.2 Name: yamllint -Version: 1.20.0 +Version: 1.22.1 Summary: A linter for YAML files. Home-page: https://github.com/adrienverge/yamllint Author: Adrien Vergé