-
Notifications
You must be signed in to change notification settings - Fork 276
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
float-values: Add a new rule to check floating-point numbers
- Loading branch information
1 parent
40cab7f
commit 8ac7d58
Showing
5 changed files
with
328 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
# Copyright (C) 2022 the yamllint contributors | ||
# | ||
# 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/>. | ||
|
||
from tests.common import RuleTestCase | ||
|
||
|
||
class FloatValuesTestCase(RuleTestCase): | ||
rule_id = 'float-values' | ||
|
||
def test_disabled(self): | ||
conf = ( | ||
'float-values: disable\n' | ||
'new-line-at-end-of-file: disable\n' | ||
'document-start: disable\n' | ||
) | ||
self.check('angle: 0.0', conf) | ||
self.check('angle: .NaN', conf) | ||
self.check('angle: .INF', conf) | ||
self.check('angle: .1', conf) | ||
self.check('angle: 10e-6', conf) | ||
self.check( | ||
'- &angle .0\n' | ||
'- *angle\n', | ||
conf, | ||
) | ||
self.check( | ||
'- &angle 10e6\n' | ||
'- *angle\n', | ||
conf, | ||
) | ||
self.check( | ||
'- &angle .nan\n' | ||
'- *angle\n', | ||
conf, | ||
) | ||
self.check( | ||
'- &angle .inf\n' | ||
'- *angle\n', | ||
conf, | ||
) | ||
|
||
def test_numeral_before_decimal(self): | ||
conf = ( | ||
'float-values:\n' | ||
' require-numeral-before-decimal: true\n' | ||
' forbid-scientific-notation: false\n' | ||
' forbid-nan: false\n' | ||
' forbid-inf: false\n' | ||
'new-line-at-end-of-file: disable\n' | ||
'document-start: disable\n' | ||
) | ||
self.check('angle: .1', conf, problem=(1, 8)) | ||
self.check('angle: 0.0', conf) | ||
self.check('angle: \'.1\'', conf) | ||
self.check('angle: !custom_tag 0.0', conf) | ||
self.check( | ||
'- &angle 0.0\n' | ||
'- *angle\n', | ||
conf | ||
) | ||
self.check( | ||
'- &angle .0\n' | ||
'- *angle\n', | ||
conf, | ||
problem=(1, 10) | ||
) | ||
|
||
def test_scientific_notation(self): | ||
conf = ( | ||
'float-values:\n' | ||
' require-numeral-before-decimal: false\n' | ||
' forbid-scientific-notation: true\n' | ||
' forbid-nan: false\n' | ||
' forbid-inf: false\n' | ||
'new-line-at-end-of-file: disable\n' | ||
'document-start: disable\n' | ||
) | ||
self.check('angle: 10e6', conf, problem=(1, 8)) | ||
self.check('angle: 10e-6', conf, problem=(1, 8)) | ||
self.check('angle: 0.00001', conf) | ||
self.check('angle: \'10e-6\'', conf) | ||
self.check('angle: !custom_tag 10e-6', conf) | ||
self.check( | ||
'- &angle 0.000001\n' | ||
'- *angle\n', | ||
conf | ||
) | ||
self.check( | ||
'- &angle 10e-6\n' | ||
'- *angle\n', | ||
conf, | ||
problem=(1, 10) | ||
) | ||
self.check( | ||
'- &angle 10e6\n' | ||
'- *angle\n', | ||
conf, | ||
problem=(1, 10) | ||
) | ||
|
||
def test_nan(self): | ||
conf = ( | ||
'float-values:\n' | ||
' require-numeral-before-decimal: false\n' | ||
' forbid-scientific-notation: false\n' | ||
' forbid-nan: true\n' | ||
' forbid-inf: false\n' | ||
'new-line-at-end-of-file: disable\n' | ||
'document-start: disable\n' | ||
) | ||
self.check('angle: .NaN', conf, problem=(1, 8)) | ||
self.check('angle: .NAN', conf, problem=(1, 8)) | ||
self.check('angle: \'.NaN\'', conf) | ||
self.check('angle: !custom_tag .NaN', conf) | ||
self.check( | ||
'- &angle .nan\n' | ||
'- *angle\n', | ||
conf, | ||
problem=(1, 10) | ||
) | ||
|
||
def test_inf(self): | ||
conf = ( | ||
'float-values:\n' | ||
' require-numeral-before-decimal: false\n' | ||
' forbid-scientific-notation: false\n' | ||
' forbid-nan: false\n' | ||
' forbid-inf: true\n' | ||
'new-line-at-end-of-file: disable\n' | ||
'document-start: disable\n' | ||
) | ||
self.check('angle: .inf', conf, problem=(1, 8)) | ||
self.check('angle: .INF', conf, problem=(1, 8)) | ||
self.check('angle: -.inf', conf, problem=(1, 8)) | ||
self.check('angle: -.INF', conf, problem=(1, 8)) | ||
self.check('angle: \'.inf\'', conf) | ||
self.check('angle: !custom_tag .inf', conf) | ||
self.check( | ||
'- &angle .inf\n' | ||
'- *angle\n', | ||
conf, | ||
problem=(1, 10) | ||
) | ||
self.check( | ||
'- &angle -.inf\n' | ||
'- *angle\n', | ||
conf, | ||
problem=(1, 10) | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
# Copyright (C) 2022 the yamllint contributors | ||
|
||
# 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 limit the permitted values for floating-point numbers. | ||
YAML permits three classes of float expressions: approximation to real numbers, | ||
positive and negative infinity and "not a number". | ||
.. rubric:: Options | ||
* Use ``require-numeral-before-decimal`` to require floats to start | ||
with a numeral (ex ``0.0`` instead of ``.0``). | ||
* Use ``forbid-scientific-notation`` to forbid scientific notation. | ||
* Use ``forbid-nan`` to forbid NaN (not a number) values. | ||
* Use ``forbid-inf`` to forbid infinite values. | ||
.. rubric:: Default values (when enabled) | ||
.. code-block:: yaml | ||
rules: | ||
float-values: | ||
forbid-inf: false | ||
forbid-nan: false | ||
forbid-scientific-notation: false | ||
require-numeral-before-decimal: false | ||
.. rubric:: Examples | ||
#. With ``float-values: {require-numeral-before-decimal: true}`` | ||
the following code snippets would **PASS**: | ||
:: | ||
anemometer: | ||
angle: 0.0 | ||
the following code snippets would **FAIL**: | ||
:: | ||
anemometer: | ||
angle: .0 | ||
#. With ``float-values: {forbid-scientific-notation: true}`` | ||
the following code snippets would **PASS**: | ||
:: | ||
anemometer: | ||
angle: 0.00001 | ||
the following code snippets would **FAIL**: | ||
:: | ||
anemometer: | ||
angle: 10e-6 | ||
#. With ``float-values: {forbid-nan: true}`` | ||
the following code snippets would **FAIL**: | ||
:: | ||
anemometer: | ||
angle: .NaN | ||
#. With ``float-values: {forbid-inf: true}`` | ||
the following code snippets would **FAIL**: | ||
:: | ||
anemometer: | ||
angle: .inf | ||
""" | ||
|
||
import re | ||
|
||
import yaml | ||
|
||
from yamllint.linter import LintProblem | ||
|
||
|
||
ID = 'float-values' | ||
TYPE = 'token' | ||
CONF = { | ||
'require-numeral-before-decimal': bool, | ||
'forbid-scientific-notation': bool, | ||
'forbid-nan': bool, | ||
'forbid-inf': bool, | ||
} | ||
DEFAULT = { | ||
'require-numeral-before-decimal': False, | ||
'forbid-scientific-notation': False, | ||
'forbid-nan': False, | ||
'forbid-inf': False, | ||
} | ||
|
||
IS_NUMERAL_BEFORE_DECIMAL_PATTERN = ( | ||
re.compile('[-+]?(\\.[0-9]+)([eE][-+]?[0-9]+)?') | ||
) | ||
IS_SCIENTIFIC_NOTATION_PATTERN = re.compile( | ||
'[-+]?(\\.[0-9]+|[0-9]+(\\.[0-9]*)?)([eE][-+]?[0-9]+)' | ||
) | ||
IS_INF_PATTERN = re.compile('[-+]?(\\.inf|\\.Inf|\\.INF)') | ||
IS_NAN_PATTERN = re.compile('\\.nan|\\.NaN|\\.NAN') | ||
|
||
|
||
def check(conf, token, prev, next, nextnext, context): | ||
if prev and isinstance(prev, yaml.tokens.TagToken): | ||
return | ||
if not isinstance(token, yaml.tokens.ScalarToken): | ||
return | ||
if token.style: | ||
return | ||
val = token.value | ||
|
||
if conf['forbid-nan'] and IS_NAN_PATTERN.match(val): | ||
yield LintProblem( | ||
token.start_mark.line + 1, | ||
token.start_mark.column + 1, | ||
'forbidden not a number value "%s"' % token.value, | ||
) | ||
|
||
if conf['forbid-inf'] and IS_INF_PATTERN.match(val): | ||
yield LintProblem( | ||
token.start_mark.line + 1, | ||
token.start_mark.column + 1, | ||
f"forbidden infinite value {token.value}", | ||
) | ||
|
||
if conf[ | ||
'forbid-scientific-notation' | ||
] and IS_SCIENTIFIC_NOTATION_PATTERN.match(val): | ||
yield LintProblem( | ||
token.start_mark.line + 1, | ||
token.start_mark.column + 1, | ||
f"forbidden scientific notation {token.value}", | ||
) | ||
|
||
if conf[ | ||
'require-numeral-before-decimal' | ||
] and IS_NUMERAL_BEFORE_DECIMAL_PATTERN.match(val): | ||
yield LintProblem( | ||
token.start_mark.line + 1, | ||
token.start_mark.column + 1, | ||
f"forbidden decimal missing 0 prefix {token.value}", | ||
) |