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 <[email protected]>
+
+- 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é