Compare commits

...

11 Commits

Author SHA1 Message Date
Adrien Vergé
0fff4e29e4 yamllint version 1.26.0 2021-01-29 18:10:35 +01:00
Adrien Vergé
1b378ed5b9 quoted-strings: Fix explicit octal recognition
PyYAML implements YAML spec version 1.1, not 1.2. Hence, values starting
with `0o` are not considered as numbers: they are just strings, so they
need quotes when `quoted-strings: {required: true}`.

>>> import yaml
>>> yaml.resolver.Resolver().resolve(yaml.nodes.ScalarNode, '100', (True, False))
'tag:yaml.org,2002:int'
>>> yaml.resolver.Resolver().resolve(yaml.nodes.ScalarNode, '0100', (True, False))
'tag:yaml.org,2002:int'
>>> yaml.resolver.Resolver().resolve(yaml.nodes.ScalarNode, '0o100', (True, False))
'tag:yaml.org,2002:str'

Let's try to prevent that.

Fixes https://github.com/adrienverge/yamllint/issues/351.
2021-01-11 16:38:29 +01:00
Adrien Vergé
a3fc64d134 End support for Python 2
As planned and advertized, yamllint drops support for Python 2 on 2021.
2021-01-06 07:55:10 +01:00
Rusty Geldmacher
ee4d163ff8 Allow only non-empty brackets/braces
We'd like to disallow brackets and braces in our YAML, but there's a
catch: the only way to describe an empty array or hash in YAML is to
supply an empty one (`[]` or `{}`). Otherwise, the value will be null.

This commit adds a `non-empty` option to `forbid` for brackets and
braces. When it is set, all flow and sequence mappings will cause errors
_except_ for empty ones.
2020-12-15 11:52:21 +01:00
Jason Mobarak
22335b294d Add support for Python 3.9, drop Python 3.4
Add support for Python 3.9 since it was officially released in October
and drop support for Python 3.4 since it is end-of-life (EOL).
2020-12-04 10:10:10 +01:00
Rex Ledesma
0f9dffde23 docs: Add configuration for integration with Arcanist 2020-10-24 13:24:55 +02:00
Mathieu Couette
cef0b48993 tests: Add unittest aliases to Python 2.7 2020-10-12 11:24:01 +02:00
Mathieu Couette
11b1f1c14e tests: Fix indentation issues 2020-10-12 11:24:01 +02:00
Mathieu Couette
9ee8c27ac9 tests: Replace deprecated aliases
https://docs.python.org/3/library/unittest.html#deprecated-aliases
2020-10-12 11:24:01 +02:00
Florian Bruhin
8eebab68ab Fix typo in changelog 2020-10-05 09:42:42 +02:00
Per Lundberg
2103bd73de README.rst: fix typo 2020-10-02 12:51:01 +02:00
18 changed files with 179 additions and 56 deletions

View File

@@ -2,27 +2,24 @@
dist: xenial # required for Python >= 3.7 (travis-ci/travis-ci#9069) dist: xenial # required for Python >= 3.7 (travis-ci/travis-ci#9069)
language: python language: python
python: python:
- 2.7
- 3.5 - 3.5
- 3.6 - 3.6
- 3.7 - 3.7
- 3.8 - 3.8
- 3.9
- nightly - nightly
env: env:
- REMOVE_LOCALES=false - REMOVE_LOCALES=false
- REMOVE_LOCALES=true - REMOVE_LOCALES=true
install: install:
- pip install pyyaml coveralls flake8 flake8-import-order doc8 - pip install pyyaml coveralls flake8 flake8-import-order doc8 sphinx
- if [[ $TRAVIS_PYTHON_VERSION != 2* ]]; then pip install sphinx; fi
- pip install . - pip install .
- if [[ $REMOVE_LOCALES = "true" ]]; then sudo rm -rf /usr/lib/locale/*; fi - if [[ $REMOVE_LOCALES = "true" ]]; then sudo rm -rf /usr/lib/locale/*; fi
script: script:
- if [[ $TRAVIS_PYTHON_VERSION != nightly ]]; then flake8 .; fi - if [[ $TRAVIS_PYTHON_VERSION != nightly ]]; then flake8 .; fi
- if [[ $TRAVIS_PYTHON_VERSION != 2* ]]; then doc8 $(git ls-files '*.rst'); fi - doc8 $(git ls-files '*.rst')
- yamllint --strict $(git ls-files '*.yaml' '*.yml') - yamllint --strict $(git ls-files '*.yaml' '*.yml')
- coverage run --source=yamllint -m unittest discover - coverage run --source=yamllint -m unittest discover
- if [[ $TRAVIS_PYTHON_VERSION != 2* ]]; then - python setup.py build_sphinx
python setup.py build_sphinx;
fi
after_success: after_success:
coveralls coveralls

View File

@@ -1,11 +1,21 @@
Changelog Changelog
========= =========
1.26.0 (2021-01-29)
-------------------
- End support for Python 2 and Python 3.4, add support for Python 3.9
- Add ``forbid: non-empty`` option to ``braces`` and ``brackets`` rules
- Fix ``quoted-strings`` for explicit octal recognition
- Add documentation for integration with Arcanist
- Fix typos in changelog and README
- Stop using deprecated ``python setup.py test`` in tests
1.25.0 (2020-09-29) 1.25.0 (2020-09-29)
------------------- -------------------
- Run tests on Travis both with and without UTF-8 locales - Run tests on Travis both with and without UTF-8 locales
- Improve documentationon with default values to rules with options - Improve documentation with default values to rules with options
- Improve documentation with a Python API usage example - Improve documentation with a Python API usage example
- Fix documentation on ``commas`` examples - Fix documentation on ``commas`` examples
- Packaging: move setuptools' configuration from ``setup.py`` to ``setup.cfg`` - Packaging: move setuptools' configuration from ``setup.py`` to ``setup.cfg``

View File

@@ -19,11 +19,7 @@ indentation, etc.
:target: https://yamllint.readthedocs.io/en/latest/?badge=latest :target: https://yamllint.readthedocs.io/en/latest/?badge=latest
:alt: Documentation status :alt: Documentation status
Written in Python (compatible with Python 2 & 3). Written in Python (compatible with Python 3 only).
⚠ 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 Documentation
------------- -------------

View File

@@ -51,3 +51,22 @@ An example workflow using GitHub Actions:
- name: Lint YAML files - name: Lint YAML files
run: yamllint . run: yamllint .
Integration with Arcanist
-------------------------
You can configure yamllint to run on ``arc lint``. Here is an example
``.arclint`` file that makes use of this configuration.
.. code:: json
{
"linters": {
"yamllint": {
"type": "script-and-regex",
"script-and-regex.script": "yamllint",
"script-and-regex.regex": "/^(?P<line>\\d+):(?P<offset>\\d+) +(?P<severity>warning|error) +(?P<message>.*) +\\((?P<name>.*)\\)$/m",
"include": "(\\.(yml|yaml)$)"
}
}
}

View File

@@ -26,14 +26,12 @@ classifiers =
Environment :: Console Environment :: Console
Intended Audience :: Developers Intended Audience :: Developers
License :: OSI Approved :: GNU General Public License v3 (GPLv3) License :: OSI Approved :: GNU General Public License v3 (GPLv3)
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3 Programming Language :: Python :: 3
Programming Language :: Python :: 3.4
Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Topic :: Software Development Topic :: Software Development
Topic :: Software Development :: Debuggers Topic :: Software Development :: Debuggers
Topic :: Software Development :: Quality Assurance Topic :: Software Development :: Quality Assurance
@@ -48,7 +46,7 @@ project_urls =
[options] [options]
packages = find: packages = find:
python_requires = >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* python_requires = >=3.5.*
include_package_data = True include_package_data = True
install_requires = install_requires =

View File

@@ -61,6 +61,30 @@ class ColonTestCase(RuleTestCase):
' a: 1\n' ' a: 1\n'
'}\n', conf, problem=(2, 8)) '}\n', conf, problem=(2, 8))
conf = ('braces:\n'
' forbid: non-empty\n')
self.check('---\n'
'dict:\n'
' a: 1\n', conf)
self.check('---\n'
'dict: {}\n', conf)
self.check('---\n'
'dict: {\n'
'}\n', conf)
self.check('---\n'
'dict: {\n'
'# commented: value\n'
'# another: value2\n'
'}\n', conf)
self.check('---\n'
'dict: {a}\n', conf, problem=(2, 8))
self.check('---\n'
'dict: {a: 1}\n', conf, problem=(2, 8))
self.check('---\n'
'dict: {\n'
' a: 1\n'
'}\n', conf, problem=(2, 8))
def test_min_spaces(self): def test_min_spaces(self):
conf = ('braces:\n' conf = ('braces:\n'
' max-spaces-inside: -1\n' ' max-spaces-inside: -1\n'

View File

@@ -60,6 +60,29 @@ class ColonTestCase(RuleTestCase):
' b\n' ' b\n'
']\n', conf, problem=(2, 9)) ']\n', conf, problem=(2, 9))
conf = ('brackets:\n'
' forbid: non-empty\n')
self.check('---\n'
'array:\n'
' - a\n'
' - b\n', conf)
self.check('---\n'
'array: []\n', conf)
self.check('---\n'
'array: [\n\n'
']\n', conf)
self.check('---\n'
'array: [\n'
'# a comment\n'
']\n', conf)
self.check('---\n'
'array: [a, b]\n', conf, problem=(2, 9))
self.check('---\n'
'array: [\n'
' a,\n'
' b\n'
']\n', conf, problem=(2, 9))
def test_min_spaces(self): def test_min_spaces(self):
conf = ('brackets:\n' conf = ('brackets:\n'
' max-spaces-inside: -1\n' ' max-spaces-inside: -1\n'

View File

@@ -14,9 +14,6 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys
import unittest
from tests.common import RuleTestCase from tests.common import RuleTestCase
@@ -159,7 +156,6 @@ class LineLengthTestCase(RuleTestCase):
' {% this line is' + 99 * ' really' + ' long %}\n', ' {% this line is' + 99 * ' really' + ' long %}\n',
conf, problem=(3, 81)) conf, problem=(3, 81))
@unittest.skipIf(sys.version_info < (3, 0), 'Python 2 not supported')
def test_unicode(self): def test_unicode(self):
conf = 'line-length: {max: 53}' conf = 'line-length: {max: 53}'
self.check('---\n' self.check('---\n'

View File

@@ -436,3 +436,21 @@ class QuotedTestCase(RuleTestCase):
'- foo bar\n' '- foo bar\n'
'- "foo bar"\n', '- "foo bar"\n',
conf, problem1=(3, 3), problem2=(7, 3), problem3=(11, 3)) conf, problem1=(3, 3), problem2=(7, 3), problem3=(11, 3))
def test_octal_values(self):
conf = 'quoted-strings: {required: true}\n'
self.check('---\n'
'- 100\n'
'- 0100\n'
'- 0o100\n'
'- 777\n'
'- 0777\n'
'- 0o777\n'
'- 800\n'
'- 0800\n'
'- 0o800\n'
'- "0800"\n'
'- "0o800"\n',
conf,
problem1=(9, 3), problem2=(10, 3))

View File

@@ -14,10 +14,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
try: from io import StringIO
from cStringIO import StringIO
except ImportError:
from io import StringIO
import fcntl import fcntl
import locale import locale
import os import os
@@ -246,19 +243,19 @@ class CommandLineTestCase(unittest.TestCase):
cli.run(()) cli.run(())
self.assertNotEqual(ctx.returncode, 0) self.assertNotEqual(ctx.returncode, 0)
self.assertEqual(ctx.stdout, '') self.assertEqual(ctx.stdout, '')
self.assertRegexpMatches(ctx.stderr, r'^usage') self.assertRegex(ctx.stderr, r'^usage')
with RunContext(self) as ctx: with RunContext(self) as ctx:
cli.run(('--unknown-arg', )) cli.run(('--unknown-arg', ))
self.assertNotEqual(ctx.returncode, 0) self.assertNotEqual(ctx.returncode, 0)
self.assertEqual(ctx.stdout, '') self.assertEqual(ctx.stdout, '')
self.assertRegexpMatches(ctx.stderr, r'^usage') self.assertRegex(ctx.stderr, r'^usage')
with RunContext(self) as ctx: with RunContext(self) as ctx:
cli.run(('-c', './conf.yaml', '-d', 'relaxed', 'file')) cli.run(('-c', './conf.yaml', '-d', 'relaxed', 'file'))
self.assertNotEqual(ctx.returncode, 0) self.assertNotEqual(ctx.returncode, 0)
self.assertEqual(ctx.stdout, '') self.assertEqual(ctx.stdout, '')
self.assertRegexpMatches( self.assertRegex(
ctx.stderr.splitlines()[-1], ctx.stderr.splitlines()[-1],
r'^yamllint: error: argument -d\/--config-data: ' r'^yamllint: error: argument -d\/--config-data: '
r'not allowed with argument -c\/--config-file$' r'not allowed with argument -c\/--config-file$'
@@ -269,21 +266,21 @@ class CommandLineTestCase(unittest.TestCase):
cli.run(('-', 'file')) cli.run(('-', 'file'))
self.assertNotEqual(ctx.returncode, 0) self.assertNotEqual(ctx.returncode, 0)
self.assertEqual(ctx.stdout, '') self.assertEqual(ctx.stdout, '')
self.assertRegexpMatches(ctx.stderr, r'^usage') self.assertRegex(ctx.stderr, r'^usage')
def test_run_with_bad_config(self): def test_run_with_bad_config(self):
with RunContext(self) as ctx: with RunContext(self) as ctx:
cli.run(('-d', 'rules: {a: b}', 'file')) cli.run(('-d', 'rules: {a: b}', 'file'))
self.assertEqual(ctx.returncode, -1) self.assertEqual(ctx.returncode, -1)
self.assertEqual(ctx.stdout, '') self.assertEqual(ctx.stdout, '')
self.assertRegexpMatches(ctx.stderr, r'^invalid config: no such rule') self.assertRegex(ctx.stderr, r'^invalid config: no such rule')
def test_run_with_empty_config(self): def test_run_with_empty_config(self):
with RunContext(self) as ctx: with RunContext(self) as ctx:
cli.run(('-d', '', 'file')) cli.run(('-d', '', 'file'))
self.assertEqual(ctx.returncode, -1) self.assertEqual(ctx.returncode, -1)
self.assertEqual(ctx.stdout, '') self.assertEqual(ctx.stdout, '')
self.assertRegexpMatches(ctx.stderr, r'^invalid config: not a dict') self.assertRegex(ctx.stderr, r'^invalid config: not a dict')
def test_run_with_config_file(self): def test_run_with_config_file(self):
with open(os.path.join(self.wd, 'config'), 'w') as f: with open(os.path.join(self.wd, 'config'), 'w') as f:
@@ -386,7 +383,7 @@ class CommandLineTestCase(unittest.TestCase):
with RunContext(self) as ctx: with RunContext(self) as ctx:
cli.run(('--version', )) cli.run(('--version', ))
self.assertEqual(ctx.returncode, 0) self.assertEqual(ctx.returncode, 0)
self.assertRegexpMatches(ctx.stdout + ctx.stderr, r'yamllint \d+\.\d+') self.assertRegex(ctx.stdout + ctx.stderr, r'yamllint \d+\.\d+')
def test_run_non_existing_file(self): def test_run_non_existing_file(self):
path = os.path.join(self.wd, 'i-do-not-exist.yaml') path = os.path.join(self.wd, 'i-do-not-exist.yaml')
@@ -395,7 +392,7 @@ class CommandLineTestCase(unittest.TestCase):
cli.run(('-f', 'parsable', path)) cli.run(('-f', 'parsable', path))
self.assertEqual(ctx.returncode, -1) self.assertEqual(ctx.returncode, -1)
self.assertEqual(ctx.stdout, '') self.assertEqual(ctx.stdout, '')
self.assertRegexpMatches(ctx.stderr, r'No such file or directory') self.assertRegex(ctx.stderr, r'No such file or directory')
def test_run_one_problem_file(self): def test_run_one_problem_file(self):
path = os.path.join(self.wd, 'a.yaml') path = os.path.join(self.wd, 'a.yaml')

View File

@@ -14,10 +14,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
try: from io import StringIO
from cStringIO import StringIO
except ImportError:
from io import StringIO
import os import os
import shutil import shutil
import sys import sys
@@ -48,7 +45,7 @@ class SimpleConfigTestCase(unittest.TestCase):
config.YamlLintConfig('not: valid: yaml') config.YamlLintConfig('not: valid: yaml')
def test_unknown_rule(self): def test_unknown_rule(self):
with self.assertRaisesRegexp( with self.assertRaisesRegex(
config.YamlLintConfigError, config.YamlLintConfigError,
'invalid config: no such rule: "this-one-does-not-exist"'): 'invalid config: no such rule: "this-one-does-not-exist"'):
config.YamlLintConfig('rules:\n' config.YamlLintConfig('rules:\n'
@@ -67,7 +64,7 @@ class SimpleConfigTestCase(unittest.TestCase):
self.assertEqual(c.rules['colons']['max-spaces-after'], 1) self.assertEqual(c.rules['colons']['max-spaces-after'], 1)
def test_unknown_option(self): def test_unknown_option(self):
with self.assertRaisesRegexp( with self.assertRaisesRegex(
config.YamlLintConfigError, config.YamlLintConfigError,
'invalid config: unknown option "abcdef" for rule "colons"'): 'invalid config: unknown option "abcdef" for rule "colons"'):
config.YamlLintConfig('rules:\n' config.YamlLintConfig('rules:\n'
@@ -105,7 +102,7 @@ class SimpleConfigTestCase(unittest.TestCase):
self.assertEqual(c.rules['indentation']['check-multi-line-strings'], self.assertEqual(c.rules['indentation']['check-multi-line-strings'],
False) False)
with self.assertRaisesRegexp( with self.assertRaisesRegex(
config.YamlLintConfigError, config.YamlLintConfigError,
'invalid config: option "indent-sequences" of "indentation" ' 'invalid config: option "indent-sequences" of "indentation" '
'should be in '): 'should be in '):

View File

@@ -47,16 +47,15 @@ class ModuleTestCase(unittest.TestCase):
subprocess.check_output([PYTHON, '-m', 'yamllint'], subprocess.check_output([PYTHON, '-m', 'yamllint'],
stderr=subprocess.STDOUT) stderr=subprocess.STDOUT)
self.assertEqual(ctx.exception.returncode, 2) self.assertEqual(ctx.exception.returncode, 2)
self.assertRegexpMatches(ctx.exception.output.decode(), self.assertRegex(ctx.exception.output.decode(), r'^usage: yamllint')
r'^usage: yamllint')
def test_run_module_on_bad_dir(self): def test_run_module_on_bad_dir(self):
with self.assertRaises(subprocess.CalledProcessError) as ctx: with self.assertRaises(subprocess.CalledProcessError) as ctx:
subprocess.check_output([PYTHON, '-m', 'yamllint', subprocess.check_output([PYTHON, '-m', 'yamllint',
'/does/not/exist'], '/does/not/exist'],
stderr=subprocess.STDOUT) stderr=subprocess.STDOUT)
self.assertRegexpMatches(ctx.exception.output.decode(), self.assertRegex(ctx.exception.output.decode(),
r'No such file or directory') r'No such file or directory')
def test_run_module_on_file(self): def test_run_module_on_file(self):
out = subprocess.check_output( out = subprocess.check_output(

View File

@@ -22,7 +22,7 @@ indentation, etc."""
APP_NAME = 'yamllint' APP_NAME = 'yamllint'
APP_VERSION = '1.25.0' APP_VERSION = '1.26.0'
APP_DESCRIPTION = __doc__ APP_DESCRIPTION = __doc__
__author__ = u'Adrien Vergé' __author__ = u'Adrien Vergé'

View File

@@ -233,7 +233,7 @@ def run(input, conf, filepath=None):
if conf.is_file_ignored(filepath): if conf.is_file_ignored(filepath):
return () return ()
if isinstance(input, (type(b''), type(u''))): # compat with Python 2 & 3 if isinstance(input, (bytes, str)):
return _run(input, conf, filepath) return _run(input, conf, filepath)
elif hasattr(input, 'read'): # Python 2's file or Python 3's io.IOBase elif hasattr(input, 'read'): # Python 2's file or Python 3's io.IOBase
# We need to have everything in memory to parse correctly # We need to have everything in memory to parse correctly

View File

@@ -22,7 +22,8 @@ braces (``{`` and ``}``).
* ``forbid`` is used to forbid the use of flow mappings which are denoted by * ``forbid`` is used to forbid the use of flow mappings which are denoted by
surrounding braces (``{`` and ``}``). Use ``true`` to forbid the use of flow surrounding braces (``{`` and ``}``). Use ``true`` to forbid the use of flow
mappings completely. mappings completely. Use ``non-empty`` to forbid the use of all flow
mappings except for empty ones.
* ``min-spaces-inside`` defines the minimal number of spaces required inside * ``min-spaces-inside`` defines the minimal number of spaces required inside
braces. braces.
* ``max-spaces-inside`` defines the maximal number of spaces allowed inside * ``max-spaces-inside`` defines the maximal number of spaces allowed inside
@@ -60,6 +61,18 @@ braces (``{`` and ``}``).
object: { key1: 4, key2: 8 } object: { key1: 4, key2: 8 }
#. With ``braces: {forbid: non-empty}``
the following code snippet would **PASS**:
::
object: {}
the following code snippet would **FAIL**:
::
object: { key1: 4, key2: 8 }
#. With ``braces: {min-spaces-inside: 0, max-spaces-inside: 0}`` #. With ``braces: {min-spaces-inside: 0, max-spaces-inside: 0}``
the following code snippet would **PASS**: the following code snippet would **PASS**:
@@ -128,7 +141,7 @@ from yamllint.rules.common import spaces_after, spaces_before
ID = 'braces' ID = 'braces'
TYPE = 'token' TYPE = 'token'
CONF = {'forbid': bool, CONF = {'forbid': (bool, 'non-empty'),
'min-spaces-inside': int, 'min-spaces-inside': int,
'max-spaces-inside': int, 'max-spaces-inside': int,
'min-spaces-inside-empty': int, 'min-spaces-inside-empty': int,
@@ -141,7 +154,15 @@ DEFAULT = {'forbid': False,
def check(conf, token, prev, next, nextnext, context): def check(conf, token, prev, next, nextnext, context):
if conf['forbid'] and isinstance(token, yaml.FlowMappingStartToken): if (conf['forbid'] is True and
isinstance(token, yaml.FlowMappingStartToken)):
yield LintProblem(token.start_mark.line + 1,
token.end_mark.column + 1,
'forbidden flow mapping')
elif (conf['forbid'] == 'non-empty' and
isinstance(token, yaml.FlowMappingStartToken) and
not isinstance(next, yaml.FlowMappingEndToken)):
yield LintProblem(token.start_mark.line + 1, yield LintProblem(token.start_mark.line + 1,
token.end_mark.column + 1, token.end_mark.column + 1,
'forbidden flow mapping') 'forbidden flow mapping')

View File

@@ -22,7 +22,8 @@ inside brackets (``[`` and ``]``).
* ``forbid`` is used to forbid the use of flow sequences which are denoted by * ``forbid`` is used to forbid the use of flow sequences which are denoted by
surrounding brackets (``[`` and ``]``). Use ``true`` to forbid the use of surrounding brackets (``[`` and ``]``). Use ``true`` to forbid the use of
flow sequences completely. flow sequences completely. Use ``non-empty`` to forbid the use of all flow
sequences except for empty ones.
* ``min-spaces-inside`` defines the minimal number of spaces required inside * ``min-spaces-inside`` defines the minimal number of spaces required inside
brackets. brackets.
* ``max-spaces-inside`` defines the maximal number of spaces allowed inside * ``max-spaces-inside`` defines the maximal number of spaces allowed inside
@@ -61,6 +62,18 @@ inside brackets (``[`` and ``]``).
object: [ 1, 2, abc ] object: [ 1, 2, abc ]
#. With ``brackets: {forbid: non-empty}``
the following code snippet would **PASS**:
::
object: []
the following code snippet would **FAIL**:
::
object: [ 1, 2, abc ]
#. With ``brackets: {min-spaces-inside: 0, max-spaces-inside: 0}`` #. With ``brackets: {min-spaces-inside: 0, max-spaces-inside: 0}``
the following code snippet would **PASS**: the following code snippet would **PASS**:
@@ -129,7 +142,7 @@ from yamllint.rules.common import spaces_after, spaces_before
ID = 'brackets' ID = 'brackets'
TYPE = 'token' TYPE = 'token'
CONF = {'forbid': bool, CONF = {'forbid': (bool, 'non-empty'),
'min-spaces-inside': int, 'min-spaces-inside': int,
'max-spaces-inside': int, 'max-spaces-inside': int,
'min-spaces-inside-empty': int, 'min-spaces-inside-empty': int,
@@ -142,7 +155,15 @@ DEFAULT = {'forbid': False,
def check(conf, token, prev, next, nextnext, context): def check(conf, token, prev, next, nextnext, context):
if conf['forbid'] and isinstance(token, yaml.FlowSequenceStartToken): if (conf['forbid'] is True and
isinstance(token, yaml.FlowSequenceStartToken)):
yield LintProblem(token.start_mark.line + 1,
token.end_mark.column + 1,
'forbidden flow sequence')
elif (conf['forbid'] == 'non-empty' and
isinstance(token, yaml.FlowSequenceStartToken) and
not isinstance(next, yaml.FlowSequenceEndToken)):
yield LintProblem(token.start_mark.line + 1, yield LintProblem(token.start_mark.line + 1,
token.end_mark.column + 1, token.end_mark.column + 1,
'forbidden flow sequence') 'forbidden flow sequence')

View File

@@ -17,10 +17,6 @@
""" """
Use this rule to set a limit to lines length. Use this rule to set a limit to lines length.
Note: with Python 2, the ``line-length`` rule may not work properly with
unicode characters because of the way strings are represented in bytes. We
recommend running yamllint with Python 3.
.. rubric:: Options .. rubric:: Options
* ``max`` defines the maximal (inclusive) length of lines. * ``max`` defines the maximal (inclusive) length of lines.

View File

@@ -144,6 +144,17 @@ def VALIDATE(conf):
DEFAULT_SCALAR_TAG = u'tag:yaml.org,2002:str' DEFAULT_SCALAR_TAG = u'tag:yaml.org,2002:str'
# https://stackoverflow.com/a/36514274
yaml.resolver.Resolver.add_implicit_resolver(
'tag:yaml.org,2002:int',
re.compile(r'''^(?:[-+]?0b[0-1_]+
|[-+]?0o?[0-7_]+
|[-+]?0[0-7_]+
|[-+]?(?:0|[1-9][0-9_]*)
|[-+]?0x[0-9a-fA-F_]+
|[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$''', re.X),
list('-+0123456789'))
def _quote_match(quote_type, token_style): def _quote_match(quote_type, token_style):
return ((quote_type == 'any') or return ((quote_type == 'any') or