summaryrefslogtreecommitdiffstats
path: root/third_party/python/yamllint/yamllint/rules/quoted_strings.py
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/python/yamllint/yamllint/rules/quoted_strings.py')
-rw-r--r--third_party/python/yamllint/yamllint/rules/quoted_strings.py230
1 files changed, 230 insertions, 0 deletions
diff --git a/third_party/python/yamllint/yamllint/rules/quoted_strings.py b/third_party/python/yamllint/yamllint/rules/quoted_strings.py
new file mode 100644
index 0000000000..1d997294da
--- /dev/null
+++ b/third_party/python/yamllint/yamllint/rules/quoted_strings.py
@@ -0,0 +1,230 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2018 ClearScore
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""
+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``).
+* ``extra-required`` is a list of PCRE regexes to force string values to be
+ quoted, if they match any regex. This option can only be used with
+ ``required: false`` and ``required: only-when-needed``.
+* ``extra-allowed`` is a list of PCRE regexes to allow quoted string values,
+ even if ``required: only-when-needed`` is set.
+
+**Note**: Multi-line strings (with ``|`` or ``>``) will not be checked.
+
+.. rubric:: Examples
+
+#. With ``quoted-strings: {quote-type: any, required: true}``
+
+ the following code snippet would **PASS**:
+ ::
+
+ foo: "bar"
+ bar: 'foo'
+ number: 123
+ boolean: true
+
+ the following code snippet would **FAIL**:
+ ::
+
+ 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'
+
+#. With ``quoted-strings: {required: false, extra-required: [^http://,
+ ^ftp://]}``
+
+ the following code snippet would **PASS**:
+ ::
+
+ - localhost
+ - "localhost"
+ - "http://localhost"
+ - "ftp://localhost"
+
+ the following code snippet would **FAIL**:
+ ::
+
+ - http://localhost
+ - ftp://localhost
+
+#. With ``quoted-strings: {required: only-when-needed, extra-allowed:
+ [^http://, ^ftp://], extra-required: [QUOTED]}``
+
+ the following code snippet would **PASS**:
+ ::
+
+ - localhost
+ - "http://localhost"
+ - "ftp://localhost"
+ - "this is a string that needs to be QUOTED"
+
+ the following code snippet would **FAIL**:
+ ::
+
+ - "localhost"
+ - this is a string that needs to be QUOTED
+"""
+
+import re
+
+import yaml
+
+from yamllint.linter import LintProblem
+
+ID = 'quoted-strings'
+TYPE = 'token'
+CONF = {'quote-type': ('any', 'single', 'double'),
+ 'required': (True, False, 'only-when-needed'),
+ 'extra-required': [str],
+ 'extra-allowed': [str]}
+DEFAULT = {'quote-type': 'any',
+ 'required': True,
+ 'extra-required': [],
+ 'extra-allowed': []}
+
+
+def VALIDATE(conf):
+ if conf['required'] is True and len(conf['extra-allowed']) > 0:
+ return 'cannot use both "required: true" and "extra-allowed"'
+ if conf['required'] is True and len(conf['extra-required']) > 0:
+ return 'cannot use both "required: true" and "extra-required"'
+ if conf['required'] is False and len(conf['extra-allowed']) > 0:
+ return 'cannot use both "required: false" and "extra-allowed"'
+
+
+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']
+
+ msg = None
+ if conf['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 conf['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.style:
+ is_extra_required = any(re.search(r, token.value)
+ for r in conf['extra-required'])
+ if is_extra_required:
+ msg = "string value is not quoted"
+
+ elif conf['required'] == 'only-when-needed':
+
+ # Quotes are not strictly needed here
+ if (token.style and tag == DEFAULT_SCALAR_TAG and token.value and
+ not _quotes_are_needed(token.value)):
+ is_extra_required = any(re.search(r, token.value)
+ for r in conf['extra-required'])
+ is_extra_allowed = any(re.search(r, token.value)
+ for r in conf['extra-allowed'])
+ if not (is_extra_required or is_extra_allowed):
+ 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
+
+ elif not token.style:
+ is_extra_required = len(conf['extra-required']) and any(
+ re.search(r, token.value) for r in conf['extra-required'])
+ if is_extra_required:
+ msg = "string value is not quoted"
+
+ if msg is not None:
+ yield LintProblem(
+ token.start_mark.line + 1,
+ token.start_mark.column + 1,
+ msg)