From d3cd8ba332f8e89314da06c0c57c53f4f43a9dc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20Verg=C3=A9?= Date: Sat, 17 Sep 2016 22:24:12 +0200 Subject: [PATCH] line-length: Generalize ...-inline-mappings for corner cases This commit refactors the `allow-non-breakable-inline-mappings` logic to use YAML tokens and avoid crashes or erroneous reports on cases like: ```yaml - {a: "http://localhost/very/very/very/very/very/very/long/url" } ``` ```yaml dict: {a: long long long long long long long, b: nospace} ``` ```yaml - long_line: http://localhost/very/very/long/url ``` ```yaml long_line: and+some+space+at+the+end <-- extra spaces ``` For reference see: https://github.com/adrienverge/yamllint/pull/17#issuecomment-247805799 --- tests/rules/test_line_length.py | 30 +++++++++++++++++++++++++++++- yamllint/rules/line_length.py | 21 ++++++++++++++++----- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/tests/rules/test_line_length.py b/tests/rules/test_line_length.py index adbd99a..2212c3f 100644 --- a/tests/rules/test_line_length.py +++ b/tests/rules/test_line_length.py @@ -113,7 +113,35 @@ class LineLengthTestCase(RuleTestCase): 'long_line: http://localhost/very/very/long/url\n' '...\n', conf, problem=(2, 21)) + conf = ('line-length: {max: 20, allow-non-breakable-words: yes}\n' + 'trailing-spaces: disable') + self.check('---\n' + 'loooooooooong+word+and+some+space+at+the+end \n', + conf, problem=(2, 21)) + + def test_non_breakable_inline_mappings(self): conf = 'line-length: {max: 20, ' \ 'allow-non-breakable-inline-mappings: yes}' self.check('---\n' - 'long_line: http://localhost/very/very/long/url\n', conf) + 'long_line: http://localhost/very/very/long/url\n' + 'long line: http://localhost/very/very/long/url\n', conf) + self.check('---\n' + '- long line: http://localhost/very/very/long/url\n', conf) + + self.check('---\n' + 'long_line: http://localhost/short/url + word\n' + 'long line: http://localhost/short/url + word\n', + conf, problem1=(2, 21), problem2=(3, 21)) + + conf = ('line-length: {max: 20,' + ' allow-non-breakable-inline-mappings: yes}\n' + 'trailing-spaces: disable') + self.check('---\n' + 'long_line: and+some+space+at+the+end \n', + conf, problem=(2, 21)) + self.check('---\n' + 'long line: and+some+space+at+the+end \n', + conf, problem=(2, 21)) + self.check('---\n' + '- long line: and+some+space+at+the+end \n', + conf, problem=(2, 21)) diff --git a/yamllint/rules/line_length.py b/yamllint/rules/line_length.py index 11e2a02..09e8b23 100644 --- a/yamllint/rules/line_length.py +++ b/yamllint/rules/line_length.py @@ -87,6 +87,7 @@ Use this rule to set a limit to lines length. http://localhost/very/very/very/very/very/very/very/very/long/url """ + import yaml from yamllint.linter import LintProblem @@ -99,6 +100,18 @@ CONF = {'max': int, 'allow-non-breakable-inline-mappings': bool} +def check_inline_mapping(line): + loader = yaml.SafeLoader(line.content) + while loader.peek_token(): + if isinstance(loader.get_token(), yaml.BlockMappingStartToken): + while loader.peek_token(): + if isinstance(loader.get_token(), yaml.ValueToken): + t = loader.get_token() + if isinstance(t, yaml.ScalarToken): + return ' ' not in line.content[t.start_mark.column:] + return False + + def check(conf, line): if line.end - line.start > conf['max']: conf['allow-non-breakable-words'] |= \ @@ -115,11 +128,9 @@ def check(conf, line): if line.buffer.find(' ', start, line.end) == -1: return - if conf['allow-non-breakable-inline-mappings']: - line_yaml = yaml.safe_load(line.content) - if (isinstance(line_yaml, dict) and - ' ' not in line_yaml.popitem()[1]): - return + if (conf['allow-non-breakable-inline-mappings'] and + check_inline_mapping(line)): + return yield LintProblem(line.line_no, conf['max'] + 1, 'line too long (%d > %d characters)' %