Compare commits

..

1 Commits

22 changed files with 92 additions and 273 deletions

View File

@@ -1,54 +0,0 @@
---
name: CI
on: # yamllint disable-line rule:truthy
push:
pull_request:
branches:
- master
jobs:
lint:
name: Linters
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.9
- run: python -m pip install flake8 flake8-import-order doc8 sphinx
- run: python -m pip install .
- run: flake8 .
- run: doc8 $(git ls-files '*.rst')
- run: yamllint --strict $(git ls-files '*.yaml' '*.yml')
- run: python setup.py build_sphinx
test:
name: Tests
runs-on: ubuntu-latest
strategy:
matrix:
python-version:
- 3.5
- 3.6
- 3.7
- 3.8
- 3.9
- nightly
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Python ${{ matrix.pyver }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.pyver }}
- name: Fix GitHub Actions path
run: echo /home/runner/.local/bin >>$GITHUB_PATH
- run: pip install coveralls
- run: pip install .
- run: coverage run --source=yamllint -m unittest discover
- name: Coveralls
uses: AndreMiras/coveralls-python-action@develop

2
.gitignore vendored
View File

@@ -5,3 +5,5 @@ __pycache__
/yamllint.egg-info
/build
/.eggs
*.yaml
!*.yaml/

28
.travis.yml Normal file
View File

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

View File

@@ -1,27 +1,6 @@
Changelog
=========
1.26.2 (2021-08-03)
-------------------
- Fix ``python_requires`` to comply with PEP 345 and PEP 440
1.26.1 (2021-04-06)
-------------------
- Remove runtime dependency ``setuptools`` for Python < 3.8
- Fix ``line_length`` to skip all hash signs starting comment
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)
-------------------

View File

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

View File

@@ -51,22 +51,3 @@ An example workflow using GitHub Actions:
- name: Lint YAML files
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,12 +26,14 @@ classifiers =
Environment :: Console
Intended Audience :: Developers
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.4
Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Topic :: Software Development
Topic :: Software Development :: Debuggers
Topic :: Software Development :: Quality Assurance
@@ -46,13 +48,13 @@ project_urls =
[options]
packages = find:
python_requires = >=3.5
python_requires = >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
include_package_data = True
install_requires =
pathspec >= 0.5.3
pyyaml
setuptools; python_version < "3.8"
setuptools
test_suite = tests
@@ -67,6 +69,3 @@ yamllint = conf/*.yaml
[options.entry_points]
console_scripts =
yamllint = yamllint.cli:run
[coverage:run]
relative_files = True

View File

@@ -61,30 +61,6 @@ class ColonTestCase(RuleTestCase):
' a: 1\n'
'}\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):
conf = ('braces:\n'
' max-spaces-inside: -1\n'

View File

@@ -60,29 +60,6 @@ class ColonTestCase(RuleTestCase):
' b\n'
']\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):
conf = ('brackets:\n'
' max-spaces-inside: -1\n'

View File

@@ -14,6 +14,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys
import unittest
from tests.common import RuleTestCase
@@ -116,27 +119,6 @@ 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: true}'
self.check('---\n'
'# http://www.verylongurlurlurlurlurlurlurlurl.com\n'
'key:\n'
' subkey: value\n', conf)
self.check('---\n'
'## http://www.verylongurlurlurlurlurlurlurlurl.com\n'
'key:\n'
' subkey: value\n', conf)
self.check('---\n'
'# # http://www.verylongurlurlurlurlurlurlurlurl.com\n'
'key:\n'
' subkey: value\n', conf,
problem=(2, 21))
self.check('---\n'
'#A http://www.verylongurlurlurlurlurlurlurlurl.com\n'
'key:\n'
' subkey: value\n', conf,
problem1=(2, 2, 'comments'),
problem2=(2, 21, 'line-length'))
conf = ('line-length: {max: 20, allow-non-breakable-words: true}\n'
'trailing-spaces: disable')
self.check('---\n'
@@ -177,6 +159,7 @@ class LineLengthTestCase(RuleTestCase):
' {% this line is' + 99 * ' really' + ' long %}\n',
conf, problem=(3, 81))
@unittest.skipIf(sys.version_info < (3, 0), 'Python 2 not supported')
def test_unicode(self):
conf = 'line-length: {max: 53}'
self.check('---\n'

View File

@@ -436,21 +436,3 @@ class QuotedTestCase(RuleTestCase):
'- foo bar\n'
'- "foo bar"\n',
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,7 +14,10 @@
# 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 io import StringIO
try:
from cStringIO import StringIO
except ImportError:
from io import StringIO
import fcntl
import locale
import os
@@ -243,19 +246,19 @@ class CommandLineTestCase(unittest.TestCase):
cli.run(())
self.assertNotEqual(ctx.returncode, 0)
self.assertEqual(ctx.stdout, '')
self.assertRegex(ctx.stderr, r'^usage')
self.assertRegexpMatches(ctx.stderr, r'^usage')
with RunContext(self) as ctx:
cli.run(('--unknown-arg', ))
self.assertNotEqual(ctx.returncode, 0)
self.assertEqual(ctx.stdout, '')
self.assertRegex(ctx.stderr, r'^usage')
self.assertRegexpMatches(ctx.stderr, r'^usage')
with RunContext(self) as ctx:
cli.run(('-c', './conf.yaml', '-d', 'relaxed', 'file'))
self.assertNotEqual(ctx.returncode, 0)
self.assertEqual(ctx.stdout, '')
self.assertRegex(
self.assertRegexpMatches(
ctx.stderr.splitlines()[-1],
r'^yamllint: error: argument -d\/--config-data: '
r'not allowed with argument -c\/--config-file$'
@@ -266,21 +269,21 @@ class CommandLineTestCase(unittest.TestCase):
cli.run(('-', 'file'))
self.assertNotEqual(ctx.returncode, 0)
self.assertEqual(ctx.stdout, '')
self.assertRegex(ctx.stderr, r'^usage')
self.assertRegexpMatches(ctx.stderr, r'^usage')
def test_run_with_bad_config(self):
with RunContext(self) as ctx:
cli.run(('-d', 'rules: {a: b}', 'file'))
self.assertEqual(ctx.returncode, -1)
self.assertEqual(ctx.stdout, '')
self.assertRegex(ctx.stderr, r'^invalid config: no such rule')
self.assertRegexpMatches(ctx.stderr, r'^invalid config: no such rule')
def test_run_with_empty_config(self):
with RunContext(self) as ctx:
cli.run(('-d', '', 'file'))
self.assertEqual(ctx.returncode, -1)
self.assertEqual(ctx.stdout, '')
self.assertRegex(ctx.stderr, r'^invalid config: not a dict')
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:
@@ -297,7 +300,6 @@ class CommandLineTestCase(unittest.TestCase):
cli.run(('-c', f.name, os.path.join(self.wd, 'a.yaml')))
self.assertEqual(ctx.returncode, 1)
@unittest.skipIf(os.environ.get('GITHUB_RUN_ID'), '$HOME not overridable')
def test_run_with_user_global_config_file(self):
home = os.path.join(self.wd, 'fake-home')
dir = os.path.join(home, '.config', 'yamllint')
@@ -384,7 +386,7 @@ class CommandLineTestCase(unittest.TestCase):
with RunContext(self) as ctx:
cli.run(('--version', ))
self.assertEqual(ctx.returncode, 0)
self.assertRegex(ctx.stdout + ctx.stderr, r'yamllint \d+\.\d+')
self.assertRegexpMatches(ctx.stdout + ctx.stderr, r'yamllint \d+\.\d+')
def test_run_non_existing_file(self):
path = os.path.join(self.wd, 'i-do-not-exist.yaml')
@@ -393,7 +395,7 @@ class CommandLineTestCase(unittest.TestCase):
cli.run(('-f', 'parsable', path))
self.assertEqual(ctx.returncode, -1)
self.assertEqual(ctx.stdout, '')
self.assertRegex(ctx.stderr, r'No such file or directory')
self.assertRegexpMatches(ctx.stderr, r'No such file or directory')
def test_run_one_problem_file(self):
path = os.path.join(self.wd, 'a.yaml')

View File

@@ -14,7 +14,10 @@
# 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 io import StringIO
try:
from cStringIO import StringIO
except ImportError:
from io import StringIO
import os
import shutil
import sys
@@ -45,7 +48,7 @@ class SimpleConfigTestCase(unittest.TestCase):
config.YamlLintConfig('not: valid: yaml')
def test_unknown_rule(self):
with self.assertRaisesRegex(
with self.assertRaisesRegexp(
config.YamlLintConfigError,
'invalid config: no such rule: "this-one-does-not-exist"'):
config.YamlLintConfig('rules:\n'
@@ -64,7 +67,7 @@ class SimpleConfigTestCase(unittest.TestCase):
self.assertEqual(c.rules['colons']['max-spaces-after'], 1)
def test_unknown_option(self):
with self.assertRaisesRegex(
with self.assertRaisesRegexp(
config.YamlLintConfigError,
'invalid config: unknown option "abcdef" for rule "colons"'):
config.YamlLintConfig('rules:\n'
@@ -102,7 +105,7 @@ class SimpleConfigTestCase(unittest.TestCase):
self.assertEqual(c.rules['indentation']['check-multi-line-strings'],
False)
with self.assertRaisesRegex(
with self.assertRaisesRegexp(
config.YamlLintConfigError,
'invalid config: option "indent-sequences" of "indentation" '
'should be in '):

View File

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

View File

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

View File

@@ -2,8 +2,11 @@
yaml-files:
- '*.yaml'
- '!*.yaml/'
- '*.yml'
- '!*.yml/'
- '.yamllint'
- '!.yamllint/'
rules:
braces: enable

View File

@@ -32,8 +32,14 @@ class YamlLintConfig(object):
self.ignore = None
self.yaml_files = pathspec.PathSpec.from_lines(
'gitwildmatch', ['*.yaml', '*.yml', '.yamllint'])
self.yaml_files = pathspec.PathSpec.from_lines('gitwildmatch', [
'*.yaml',
'!*.yaml/',
'*.yml',
'!*.yml/',
'.yamllint',
'!.yamllint/',
])
self.locale = None
@@ -48,7 +54,7 @@ class YamlLintConfig(object):
return self.ignore and self.ignore.match_file(filepath)
def is_yaml_file(self, filepath):
return self.yaml_files.match_file(os.path.basename(filepath))
return self.yaml_files.match_file(filepath)
def enabled_rules(self, filepath):
return [yamllint.rules.get(id) for id, val in self.rules.items()

View File

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

View File

@@ -22,8 +22,7 @@ braces (``{`` and ``}``).
* ``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
mappings completely. Use ``non-empty`` to forbid the use of all flow
mappings except for empty ones.
mappings completely.
* ``min-spaces-inside`` defines the minimal number of spaces required inside
braces.
* ``max-spaces-inside`` defines the maximal number of spaces allowed inside
@@ -61,18 +60,6 @@ braces (``{`` and ``}``).
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}``
the following code snippet would **PASS**:
@@ -141,7 +128,7 @@ from yamllint.rules.common import spaces_after, spaces_before
ID = 'braces'
TYPE = 'token'
CONF = {'forbid': (bool, 'non-empty'),
CONF = {'forbid': bool,
'min-spaces-inside': int,
'max-spaces-inside': int,
'min-spaces-inside-empty': int,
@@ -154,15 +141,7 @@ DEFAULT = {'forbid': False,
def check(conf, token, prev, next, nextnext, context):
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)):
if conf['forbid'] and isinstance(token, yaml.FlowMappingStartToken):
yield LintProblem(token.start_mark.line + 1,
token.end_mark.column + 1,
'forbidden flow mapping')

View File

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

View File

@@ -17,6 +17,10 @@
"""
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
* ``max`` defines the maximal (inclusive) length of lines.
@@ -140,11 +144,7 @@ def check(conf, line):
start += 1
if start != line.end:
if line.buffer[start] == '#':
while line.buffer[start] == '#':
start += 1
start += 1
elif line.buffer[start] == '-':
if line.buffer[start] in ('#', '-'):
start += 2
if line.buffer.find(' ', start, line.end) == -1:

View File

@@ -144,17 +144,6 @@ def VALIDATE(conf):
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):
return ((quote_type == 'any') or