├── .flake8 ├── .github └── workflows │ ├── publish.yaml │ └── tests.yaml ├── .gitignore ├── .pre-commit-hooks.yaml ├── .readthedocs.yaml ├── CHANGELOG.rst ├── CONTRIBUTING.rst ├── LICENSE ├── MANIFEST.in ├── README.rst ├── docs ├── Makefile ├── character_encoding.rst ├── conf.py ├── configuration.rst ├── development.rst ├── disable_with_comments.rst ├── index.rst ├── integration.rst ├── quickstart.rst ├── requirements.txt ├── rules.rst ├── screenshot.png └── text_editors.rst ├── pyproject.toml ├── setup.py ├── tests ├── __init__.py ├── common.py ├── rules │ ├── __init__.py │ ├── test_anchors.py │ ├── test_braces.py │ ├── test_brackets.py │ ├── test_colons.py │ ├── test_commas.py │ ├── test_comments.py │ ├── test_comments_indentation.py │ ├── test_common.py │ ├── test_document_end.py │ ├── test_document_start.py │ ├── test_empty_lines.py │ ├── test_empty_values.py │ ├── test_float_values.py │ ├── test_hyphens.py │ ├── test_indentation.py │ ├── test_key_duplicates.py │ ├── test_key_ordering.py │ ├── test_line_length.py │ ├── test_new_line_at_end_of_file.py │ ├── test_new_lines.py │ ├── test_octal_values.py │ ├── test_quoted_strings.py │ ├── test_trailing_spaces.py │ └── test_truthy.py ├── test_cli.py ├── test_config.py ├── test_decoder.py ├── test_linter.py ├── test_module.py ├── test_parser.py ├── test_spec_examples.py ├── test_syntax_errors.py ├── test_yamllint_directives.py └── yaml-1.2-spec-examples │ ├── example-10.1 │ ├── example-10.2 │ ├── example-10.3 │ ├── example-10.4 │ ├── example-10.5 │ ├── example-10.6 │ ├── example-10.7 │ ├── example-10.8 │ ├── example-10.9 │ ├── example-2.1 │ ├── example-2.10 │ ├── example-2.11 │ ├── example-2.12 │ ├── example-2.13 │ ├── example-2.14 │ ├── example-2.15 │ ├── example-2.16 │ ├── example-2.17 │ ├── example-2.18 │ ├── example-2.19 │ ├── example-2.2 │ ├── example-2.20 │ ├── example-2.21 │ ├── example-2.22 │ ├── example-2.23 │ ├── example-2.24 │ ├── example-2.25 │ ├── example-2.26 │ ├── example-2.27 │ ├── example-2.28 │ ├── example-2.3 │ ├── example-2.4 │ ├── example-2.5 │ ├── example-2.6 │ ├── example-2.7 │ ├── example-2.8 │ ├── example-2.9 │ ├── example-5.1 │ ├── example-5.10 │ ├── example-5.11 │ ├── example-5.12 │ ├── example-5.13 │ ├── example-5.14 │ ├── example-5.2 │ ├── example-5.3 │ ├── example-5.4 │ ├── example-5.5 │ ├── example-5.6 │ ├── example-5.7 │ ├── example-5.8 │ ├── example-5.9 │ ├── example-6.1 │ ├── example-6.10 │ ├── example-6.11 │ ├── example-6.12 │ ├── example-6.13 │ ├── example-6.14 │ ├── example-6.15 │ ├── example-6.16 │ ├── example-6.17 │ ├── example-6.18 │ ├── example-6.19 │ ├── example-6.2 │ ├── example-6.20 │ ├── example-6.21 │ ├── example-6.22 │ ├── example-6.23 │ ├── example-6.24 │ ├── example-6.25 │ ├── example-6.26 │ ├── example-6.27 │ ├── example-6.28 │ ├── example-6.29 │ ├── example-6.3 │ ├── example-6.4 │ ├── example-6.5 │ ├── example-6.6 │ ├── example-6.7 │ ├── example-6.8 │ ├── example-6.9 │ ├── example-7.1 │ ├── example-7.10 │ ├── example-7.11 │ ├── example-7.12 │ ├── example-7.13 │ ├── example-7.14 │ ├── example-7.15 │ ├── example-7.16 │ ├── example-7.17 │ ├── example-7.18 │ ├── example-7.19 │ ├── example-7.2 │ ├── example-7.20 │ ├── example-7.21 │ ├── example-7.22 │ ├── example-7.23 │ ├── example-7.24 │ ├── example-7.3 │ ├── example-7.4 │ ├── example-7.5 │ ├── example-7.6 │ ├── example-7.7 │ ├── example-7.8 │ ├── example-7.9 │ ├── example-8.1 │ ├── example-8.10 │ ├── example-8.11 │ ├── example-8.12 │ ├── example-8.13 │ ├── example-8.14 │ ├── example-8.15 │ ├── example-8.16 │ ├── example-8.17 │ ├── example-8.18 │ ├── example-8.19 │ ├── example-8.2 │ ├── example-8.20 │ ├── example-8.21 │ ├── example-8.22 │ ├── example-8.3 │ ├── example-8.4 │ ├── example-8.5 │ ├── example-8.6 │ ├── example-8.7 │ ├── example-8.8 │ ├── example-8.9 │ ├── example-9.1 │ ├── example-9.2 │ ├── example-9.3 │ ├── example-9.4 │ ├── example-9.5 │ └── example-9.6 └── yamllint ├── __init__.py ├── __main__.py ├── cli.py ├── conf ├── default.yaml └── relaxed.yaml ├── config.py ├── decoder.py ├── linter.py ├── parser.py └── rules ├── __init__.py ├── anchors.py ├── braces.py ├── brackets.py ├── colons.py ├── commas.py ├── comments.py ├── comments_indentation.py ├── common.py ├── document_end.py ├── document_start.py ├── empty_lines.py ├── empty_values.py ├── float_values.py ├── hyphens.py ├── indentation.py ├── key_duplicates.py ├── key_ordering.py ├── line_length.py ├── new_line_at_end_of_file.py ├── new_lines.py ├── octal_values.py ├── quoted_strings.py ├── trailing_spaces.py └── truthy.py /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | import-order-style = pep8 3 | application-import-names = yamllint 4 | ignore = W503,W504 5 | -------------------------------------------------------------------------------- /.github/workflows/publish.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | name: Publish 4 | 5 | on: push # yamllint disable-line rule:truthy 6 | 7 | jobs: 8 | build: 9 | name: Build distribution package 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | with: 14 | persist-credentials: false 15 | - name: Fetch tags 16 | if: github.ref_type != 'tag' 17 | run: git fetch --prune --unshallow --tags 18 | - name: Set up Python 19 | uses: actions/setup-python@v5 20 | with: 21 | python-version: "3.x" 22 | - name: Install pypa/build 23 | run: python -m pip install build twine 24 | - name: Add '.devN' to version for non-tag builds 25 | if: github.ref_type != 'tag' 26 | run: 27 | N=$(git describe --tags --long | cut -d- -f2); 28 | sed -i 29 | "/^APP_VERSION = /s/'$/.dev$N'/" 30 | yamllint/__init__.py 31 | - name: Build a binary wheel and a source tarball 32 | run: python -m build 33 | - name: Twine check the distribution packages 34 | run: python -Im twine check --strict dist/yamllint-* 35 | - name: Store the distribution packages 36 | uses: actions/upload-artifact@v4 37 | with: 38 | name: python-package-distributions 39 | path: dist/ 40 | 41 | publish-to-testpypi: 42 | name: Publish distribution package to TestPyPI 43 | if: github.ref_name == github.event.repository.default_branch 44 | needs: build 45 | runs-on: ubuntu-latest 46 | environment: 47 | name: testpypi 48 | url: https://test.pypi.org/p/yamllint 49 | permissions: 50 | id-token: write 51 | steps: 52 | - name: Download all the dists 53 | uses: actions/download-artifact@v4 54 | with: 55 | name: python-package-distributions 56 | path: dist/ 57 | - name: Publish distribution package 58 | uses: pypa/gh-action-pypi-publish@release/v1 59 | with: 60 | repository-url: https://test.pypi.org/legacy/ 61 | 62 | publish-to-pypi: 63 | name: Publish distribution package to PyPI 64 | if: github.ref_type == 'tag' 65 | needs: build 66 | runs-on: ubuntu-latest 67 | environment: 68 | name: pypi 69 | url: https://pypi.org/p/yamllint 70 | permissions: 71 | id-token: write 72 | steps: 73 | - name: Download all the dists 74 | uses: actions/download-artifact@v4 75 | with: 76 | name: python-package-distributions 77 | path: dist/ 78 | - name: Publish distribution package 79 | uses: pypa/gh-action-pypi-publish@release/v1 80 | 81 | github-release: 82 | name: Sign and upload GitHub Release 83 | needs: publish-to-pypi 84 | runs-on: ubuntu-latest 85 | permissions: 86 | contents: write 87 | id-token: write 88 | steps: 89 | - name: Download all the dists 90 | uses: actions/download-artifact@v4 91 | with: 92 | name: python-package-distributions 93 | path: dist/ 94 | - name: Sign the dists with Sigstore 95 | uses: sigstore/gh-action-sigstore-python@v3.0.0 96 | with: 97 | inputs: dist/yamllint-*.tar.gz dist/yamllint-*.whl 98 | - name: Create GitHub Release 99 | env: 100 | GITHUB_TOKEN: ${{ github.token }} 101 | run: 102 | gh release create 103 | "$GITHUB_REF_NAME" 104 | --repo "$GITHUB_REPOSITORY" 105 | --notes "" 106 | - name: Upload artifact signatures to GitHub Release 107 | env: 108 | GITHUB_TOKEN: ${{ github.token }} 109 | run: 110 | gh release upload 111 | "$GITHUB_REF_NAME" 112 | dist/** 113 | --repo "$GITHUB_REPOSITORY" 114 | -------------------------------------------------------------------------------- /.github/workflows/tests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | name: Tests 4 | 5 | on: # yamllint disable-line rule:truthy 6 | push: 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | permissions: 12 | contents: read 13 | 14 | jobs: 15 | lint: 16 | name: Linters 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v4 21 | - name: Set up Python 22 | uses: actions/setup-python@v5 23 | - run: 24 | pip install flake8 flake8-import-order sphinx sphinx_rtd_theme 25 | rstcheck[sphinx] doc8 26 | - run: pip install . 27 | - run: flake8 . 28 | - run: doc8 $(git ls-files '*.rst') 29 | - run: rstcheck --ignore-directives automodule $(git ls-files '*.rst') 30 | - run: yamllint --strict $(git ls-files '*.yaml' '*.yml') 31 | - run: make -C docs html 32 | - name: Check for broken links in documentation 33 | run: make -C docs linkcheck 34 | 35 | test: 36 | name: Unit tests 37 | runs-on: ubuntu-latest 38 | strategy: 39 | fail-fast: false 40 | matrix: 41 | python-version: 42 | - '3.9' 43 | - '3.10' 44 | - '3.11' 45 | - '3.12' 46 | - '3.13' 47 | steps: 48 | - name: Checkout 49 | uses: actions/checkout@v4 50 | - name: Set up Python ${{ matrix.python-version }} 51 | uses: actions/setup-python@v5 52 | with: 53 | python-version: ${{ matrix.python-version }} 54 | - name: Append GitHub Actions system path 55 | run: echo "$HOME/.local/bin" >> $GITHUB_PATH 56 | - run: pip install coverage 57 | - run: pip install . 58 | # https://github.com/AndreMiras/coveralls-python-action/issues/18 59 | - run: echo -e "[run]\nrelative_files = True" > .coveragerc 60 | - run: >- 61 | python 62 | -X warn_default_encoding 63 | -W error::EncodingWarning 64 | -m coverage 65 | run 66 | -m unittest 67 | discover 68 | - name: Coveralls 69 | uses: AndreMiras/coveralls-python-action@develop 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.py[cod] 3 | /docs/_build 4 | /dist 5 | /yamllint.egg-info 6 | /build 7 | /.eggs 8 | -------------------------------------------------------------------------------- /.pre-commit-hooks.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # For use with pre-commit. 4 | # See usage instructions at https://pre-commit.com 5 | 6 | - id: yamllint 7 | name: yamllint 8 | description: This hook runs yamllint. 9 | entry: yamllint 10 | language: python 11 | types: [file, yaml] 12 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | build: 4 | os: ubuntu-22.04 5 | tools: 6 | python: "3.12" 7 | sphinx: 8 | configuration: docs/conf.py 9 | python: 10 | install: 11 | - requirements: docs/requirements.txt 12 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | Pull requests are the best way to propose changes to the codebase. 5 | Contributions are welcome, but they have to meet some criteria. 6 | 7 | Pull Request Process 8 | -------------------- 9 | 10 | 1. Fork this Git repository and create your branch from ``master``. 11 | 12 | 2. Make sure the tests pass: 13 | 14 | .. code:: bash 15 | 16 | pip install --user . 17 | python -m unittest discover # all tests... 18 | python -m unittest tests/rules/test_commas.py # or just some tests (faster) 19 | 20 | 3. If you add code that should be tested, add tests. 21 | 22 | 4. Make sure the linters pass: 23 | 24 | .. code:: bash 25 | 26 | flake8 . 27 | 28 | If you added/modified documentation: 29 | 30 | .. code:: bash 31 | 32 | doc8 $(git ls-files '*.rst') 33 | 34 | If you touched YAML files: 35 | 36 | .. code:: bash 37 | 38 | yamllint --strict $(git ls-files '*.yaml' '*.yml') 39 | 40 | 5. If relevant, update documentation (either in ``docs`` directly or in rules 41 | files themselves). 42 | 43 | 6. Write a `good commit message 44 | `_. 45 | If the pull request has multiple commits, each must be atomic (single 46 | irreducible change that makes sense on its own). 47 | 48 | 7. Then, open a pull request. 49 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.rst 2 | include docs/Makefile docs/*.py docs/*.rst docs/*.png 3 | include tests/*.py tests/rules/*.py tests/yaml-1.2-spec-examples/example-* 4 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | yamllint 2 | ======== 3 | 4 | A linter for YAML files. 5 | 6 | yamllint does not only check for syntax validity, but for weirdnesses like key 7 | repetition and cosmetic problems such as lines length, trailing spaces, 8 | indentation, etc. 9 | 10 | .. image:: 11 | https://github.com/adrienverge/yamllint/actions/workflows/tests.yaml/badge.svg?branch=master 12 | :target: https://github.com/adrienverge/yamllint/actions/workflows/tests.yaml?query=branch%3Amaster 13 | :alt: CI tests status 14 | .. image:: 15 | https://coveralls.io/repos/github/adrienverge/yamllint/badge.svg?branch=master 16 | :target: https://coveralls.io/github/adrienverge/yamllint?branch=master 17 | :alt: Code coverage status 18 | .. image:: https://readthedocs.org/projects/yamllint/badge/?version=latest 19 | :target: https://yamllint.readthedocs.io/en/latest/?badge=latest 20 | :alt: Documentation status 21 | 22 | Written in Python (compatible with Python 3 only). 23 | 24 | Documentation 25 | ------------- 26 | 27 | https://yamllint.readthedocs.io/ 28 | 29 | Overview 30 | -------- 31 | 32 | Screenshot 33 | ^^^^^^^^^^ 34 | 35 | .. image:: docs/screenshot.png 36 | :alt: yamllint screenshot 37 | 38 | Installation 39 | ^^^^^^^^^^^^ 40 | 41 | Using pip, the Python package manager: 42 | 43 | .. code:: bash 44 | 45 | pip install --user yamllint 46 | 47 | yamllint is also packaged for all major operating systems, see installation 48 | examples (``dnf``, ``apt-get``...) `in the documentation 49 | `_. 50 | 51 | Usage 52 | ^^^^^ 53 | 54 | .. code:: bash 55 | 56 | # Lint one or more files 57 | yamllint my_file.yml my_other_file.yaml ... 58 | 59 | .. code:: bash 60 | 61 | # Lint all YAML files in a directory 62 | yamllint . 63 | 64 | .. code:: bash 65 | 66 | # Use a pre-defined lint configuration 67 | yamllint -d relaxed file.yaml 68 | 69 | # Use a custom lint configuration 70 | yamllint -c /path/to/myconfig file-to-lint.yaml 71 | 72 | .. code:: bash 73 | 74 | # Output a parsable format (for syntax checking in editors like Vim, emacs...) 75 | yamllint -f parsable file.yaml 76 | 77 | `Read more in the complete documentation! `__ 78 | 79 | Features 80 | ^^^^^^^^ 81 | 82 | Here is a yamllint configuration file example: 83 | 84 | .. code:: yaml 85 | 86 | extends: default 87 | 88 | rules: 89 | # 80 chars should be enough, but don't fail if a line is longer 90 | line-length: 91 | max: 80 92 | level: warning 93 | 94 | # don't bother me with this rule 95 | indentation: disable 96 | 97 | Within a YAML file, special comments can be used to disable checks for a single 98 | line: 99 | 100 | .. code:: yaml 101 | 102 | This line is waaaaaaaaaay too long # yamllint disable-line 103 | 104 | or for a whole block: 105 | 106 | .. code:: yaml 107 | 108 | # yamllint disable rule:colons 109 | - Lorem : ipsum 110 | dolor : sit amet, 111 | consectetur : adipiscing elit 112 | # yamllint enable 113 | 114 | Specific files can be ignored (totally or for some rules only) using a 115 | ``.gitignore``-style pattern: 116 | 117 | .. code:: yaml 118 | 119 | # For all rules 120 | ignore: | 121 | *.dont-lint-me.yaml 122 | /bin/ 123 | !/bin/*.lint-me-anyway.yaml 124 | 125 | rules: 126 | key-duplicates: 127 | ignore: | 128 | generated 129 | *.template.yaml 130 | trailing-spaces: 131 | ignore: | 132 | *.ignore-trailing-spaces.yaml 133 | /ascii-art/* 134 | 135 | `Read more in the complete documentation! `__ 136 | 137 | License 138 | ------- 139 | 140 | `GPL version 3 `_ 141 | -------------------------------------------------------------------------------- /docs/character_encoding.rst: -------------------------------------------------------------------------------- 1 | Character Encoding 2 | ================== 3 | 4 | When yamllint reads a file (whether its a configuration file or a file to 5 | lint), yamllint will try to automatically detect that file’s character 6 | encoding. In order for the automatic detection to work properly, files must 7 | follow these two rules (see `this section of the YAML specification for details 8 | `_): 9 | 10 | * The file must be encoded in UTF-8, UTF-16 or UTF-32. 11 | 12 | * The file must begin with either a byte order mark or an ASCII character. 13 | 14 | Override character encoding 15 | --------------------------- 16 | 17 | Previous versions of yamllint did not try to autodetect the character encoding 18 | of files. Previous versions of yamllint assumed that files used the current 19 | locale’s character encoding. This meant that older versions of yamllint would 20 | sometimes correctly decode files that didn’t follow those two rules. For the 21 | sake of backwards compatibility, the current version of yamllint allows you to 22 | disable automatic character encoding detection by setting the 23 | ``YAMLLINT_FILE_ENCODING`` environment variable. If you set the 24 | ``YAMLLINT_FILE_ENCODING`` environment variable to the `the name of one of 25 | Python’s standard character encodings 26 | `_, then 27 | yamllint will use that character encoding instead of trying to autodetect the 28 | character encoding. 29 | 30 | The ``YAMLLINT_FILE_ENCODING`` environment variable should only be used as a 31 | stopgap solution. If you need to use ``YAMLLINT_FILE_ENCODING``, then you 32 | should really update your YAML files so that their character encoding can 33 | automatically be detected, or else you may run into compatibility problems. 34 | Future versions of yamllint may remove support for the 35 | ``YAMLLINT_FILE_ENCODING`` environment variable, and other YAML processors may 36 | misinterpret your YAML files. 37 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # yamllint documentation build configuration file, created by 2 | # sphinx-quickstart on Thu Jan 21 21:18:52 2016. 3 | 4 | import os 5 | import sys 6 | from unittest.mock import MagicMock 7 | 8 | sys.path.insert(0, os.path.abspath('..')) 9 | from yamllint import __copyright__, APP_NAME, APP_VERSION # noqa: E402 10 | 11 | # -- General configuration ------------------------------------------------ 12 | 13 | extensions = [ 14 | 'sphinx_rtd_theme', 15 | 'sphinx.ext.autodoc', 16 | ] 17 | 18 | source_suffix = '.rst' 19 | 20 | master_doc = 'index' 21 | 22 | project = APP_NAME 23 | copyright = __copyright__.lstrip('Copyright ') 24 | 25 | version = APP_VERSION 26 | release = APP_VERSION 27 | 28 | pygments_style = 'sphinx' 29 | 30 | # -- Options for HTML output ---------------------------------------------- 31 | 32 | html_theme = 'sphinx_rtd_theme' 33 | 34 | htmlhelp_basename = 'yamllintdoc' 35 | 36 | # -- Options for manual page output --------------------------------------- 37 | 38 | # One entry per manual page. List of tuples 39 | # (source start file, name, description, authors, manual section). 40 | man_pages = [ 41 | ('index', 'yamllint', 'Linter for YAML files', ['Adrien Vergé'], 1) 42 | ] 43 | 44 | # -- Build with sphinx automodule without needing to install third-party libs 45 | 46 | 47 | class Mock(MagicMock): 48 | @classmethod 49 | def __getattr__(cls, name): 50 | return MagicMock() 51 | 52 | 53 | MOCK_MODULES = ['pathspec', 'yaml'] 54 | sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES) 55 | -------------------------------------------------------------------------------- /docs/development.rst: -------------------------------------------------------------------------------- 1 | Development 2 | =========== 3 | 4 | yamllint provides both a script and a Python module. The latter can be used to 5 | write your own linting tools. 6 | 7 | Basic example of running the linter from Python: 8 | 9 | .. code-block:: python 10 | 11 | import yamllint.config 12 | import yamllint.linter 13 | 14 | yaml_config = yamllint.config.YamlLintConfig("extends: default") 15 | for p in yamllint.linter.run(open("example.yaml", "r"), yaml_config): 16 | print(p.desc, p.line, p.rule) 17 | 18 | .. automodule:: yamllint.linter 19 | :members: 20 | -------------------------------------------------------------------------------- /docs/disable_with_comments.rst: -------------------------------------------------------------------------------- 1 | Disable with comments 2 | ===================== 3 | 4 | Disabling checks for a specific line 5 | ------------------------------------ 6 | 7 | To prevent yamllint from reporting problems for a specific line, add a 8 | directive comment (``# yamllint disable-line ...``) on that line, or on the 9 | line above. For instance: 10 | 11 | .. code-block:: yaml 12 | 13 | # The following mapping contains the same key twice, 14 | # but I know what I'm doing: 15 | - key: value 1 16 | key: value 2 # yamllint disable-line rule:key-duplicates 17 | 18 | - This line is waaaaaaaaaay too long but yamllint will not report anything about it. # yamllint disable-line rule:line-length 19 | - This line will be checked by yamllint. 20 | 21 | or: 22 | 23 | .. code-block:: yaml 24 | 25 | # The following mapping contains the same key twice, 26 | # but I know what I'm doing: 27 | - key: value 1 28 | # yamllint disable-line rule:key-duplicates 29 | key: value 2 30 | 31 | # yamllint disable-line rule:line-length 32 | - This line is waaaaaaaaaay too long but yamllint will not report anything about it. 33 | - This line will be checked by yamllint. 34 | 35 | It is possible, although not recommend, to disabled **all** rules for a 36 | specific line: 37 | 38 | .. code-block:: yaml 39 | 40 | # yamllint disable-line 41 | - { all : rules ,are disabled for this line} 42 | 43 | You can't make yamllint ignore invalid YAML syntax on a line (which generates a 44 | `syntax error`), such as when templating a YAML file with Jinja. In some cases, 45 | you can workaround this by putting the templating syntax in a YAML comment. See 46 | `Putting template flow control in comments`_. 47 | 48 | If you need to disable multiple rules, it is allowed to chain rules like this: 49 | ``# yamllint disable-line rule:hyphens rule:commas rule:indentation``. 50 | 51 | Disabling checks for all (or part of) the file 52 | ---------------------------------------------- 53 | 54 | To prevent yamllint from reporting problems for the whole file, or for a block 55 | of lines within the file, use ``# yamllint disable ...`` and ``# yamllint 56 | enable ...`` directive comments. For instance: 57 | 58 | .. code-block:: yaml 59 | 60 | # yamllint disable rule:colons 61 | - Lorem : ipsum 62 | dolor : sit amet, 63 | consectetur : adipiscing elit 64 | # yamllint enable rule:colons 65 | 66 | - rest of the document... 67 | 68 | It is possible, although not recommend, to disabled **all** rules: 69 | 70 | .. code-block:: yaml 71 | 72 | # yamllint disable 73 | - Lorem : 74 | ipsum: 75 | dolor : [ sit,amet] 76 | - consectetur : adipiscing elit 77 | # yamllint enable 78 | 79 | If you need to disable multiple rules, it is allowed to chain rules like this: 80 | ``# yamllint disable rule:hyphens rule:commas rule:indentation``. 81 | 82 | Disabling all checks for a file 83 | ------------------------------- 84 | 85 | To prevent yamllint from reporting problems for a specific file, add the 86 | directive comment ``# yamllint disable-file`` as the first line of the file. 87 | For instance: 88 | 89 | .. code-block:: yaml 90 | 91 | # yamllint disable-file 92 | # The following mapping contains the same key twice, but I know what I'm doing: 93 | - key: value 1 94 | key: value 2 95 | 96 | - This line is waaaaaaaaaay too long but yamllint will not report anything about it. 97 | 98 | or: 99 | 100 | .. code-block:: jinja 101 | 102 | # yamllint disable-file 103 | # This file is not valid YAML because it is a Jinja template 104 | {% if extra_info %} 105 | key1: value1 106 | {% endif %} 107 | key2: value2 108 | 109 | Putting template flow control in comments 110 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 111 | 112 | Alternatively for templating you can wrap the template statements in comments 113 | to make it a valid YAML file. As long as the templating language doesn't use 114 | the same comment symbol, it should be a valid template and valid YAML (pre and 115 | post-template processing). 116 | 117 | Example of a Jinja2 code that cannot be parsed as YAML because it contains 118 | invalid tokens ``{%`` and ``%}``: 119 | 120 | .. code-block:: text 121 | 122 | # This file IS NOT valid YAML and will produce syntax errors 123 | {% if extra_info %} 124 | key1: value1 125 | {% endif %} 126 | key2: value2 127 | 128 | But it can be fixed using YAML comments: 129 | 130 | .. code-block:: yaml 131 | 132 | # This file IS valid YAML because the Jinja is in a YAML comment 133 | # {% if extra_info %} 134 | key1: value1 135 | # {% endif %} 136 | key2: value2 137 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | yamllint documentation 2 | ====================== 3 | 4 | .. automodule:: yamllint 5 | 6 | Screenshot 7 | ---------- 8 | 9 | .. image:: screenshot.png 10 | :alt: yamllint screenshot 11 | 12 | .. note:: 13 | 14 | The default output format is inspired by `eslint `_, a 15 | great linting tool for Javascript. 16 | 17 | Table of contents 18 | ----------------- 19 | 20 | .. toctree:: 21 | :maxdepth: 2 22 | 23 | quickstart 24 | configuration 25 | rules 26 | disable_with_comments 27 | development 28 | text_editors 29 | integration 30 | character_encoding 31 | -------------------------------------------------------------------------------- /docs/integration.rst: -------------------------------------------------------------------------------- 1 | Integration with other software 2 | =============================== 3 | 4 | Integration with pre-commit 5 | --------------------------- 6 | 7 | You can integrate yamllint in the `pre-commit `_ tool. 8 | Here is an example, to add in your .pre-commit-config.yaml 9 | 10 | .. code:: yaml 11 | 12 | --- 13 | # Update the rev variable with the release version that you want, from the yamllint repo 14 | # You can pass your custom .yamllint with args attribute. 15 | repos: 16 | - repo: https://github.com/adrienverge/yamllint.git 17 | rev: v1.29.0 18 | hooks: 19 | - id: yamllint 20 | args: [--strict, -c=/path/to/.yamllint] 21 | 22 | 23 | Integration with GitHub Actions 24 | ------------------------------- 25 | 26 | yamllint auto-detects when it's running inside of `GitHub 27 | Actions `_ and automatically uses the 28 | suited output format to decorate code with linting errors. You can also force 29 | the GitHub Actions output with ``yamllint --format github``. 30 | 31 | A minimal example workflow using GitHub Actions: 32 | 33 | .. code:: yaml 34 | 35 | --- 36 | on: push # yamllint disable-line rule:truthy 37 | 38 | jobs: 39 | lint: 40 | runs-on: ubuntu-latest 41 | steps: 42 | - uses: actions/checkout@v3 43 | 44 | - name: Install yamllint 45 | run: pip install yamllint 46 | 47 | - name: Lint YAML files 48 | run: yamllint . 49 | 50 | Integration with GitLab 51 | ----------------------- 52 | 53 | You can use the following GitLab CI/CD stage to run yamllint and get the 54 | results as a `Code quality (Code Climate) 55 | `_ report. 56 | 57 | .. code:: yaml 58 | 59 | --- 60 | lint: 61 | stage: lint 62 | script: 63 | - pip install yamllint 64 | - mkdir reports 65 | - > 66 | yamllint -f parsable . | tee >(awk ' 67 | BEGIN {FS = ":"; ORS="\n"; first=1} 68 | { 69 | gsub(/^[ \t]+|[ \t]+$|"/, "", $4); 70 | match($4, /^\[(warning|error)\](.*)\((.*)\)$/, a); 71 | sev = (a[1] == "error" ? "major" : "minor"); 72 | if (first) { 73 | first=0; 74 | printf("["); 75 | } else { 76 | printf(","); 77 | } 78 | printf("{\"location\":{\"path\":\"%s\",\"lines\":{\"begin\":%s,"\ 79 | "\"end\":%s}},\"severity\":\"%s\",\"check_name\":\"%s\","\ 80 | "\"categories\":[\"Style\"],\"type\":\"issue\","\ 81 | "\"description\":\"%s\"}", $1, $2, $3, sev, a[3], a[2]); 82 | } 83 | END { if (!first) printf("]\n"); }' > reports/codequality.json) 84 | artifacts: 85 | when: always 86 | paths: 87 | - reports 88 | expire_in: 1 week 89 | reports: 90 | codequality: reports/codequality.json 91 | 92 | Integration with Arcanist 93 | ------------------------- 94 | 95 | You can configure yamllint to run on ``arc lint``. Here is an example 96 | ``.arclint`` file that makes use of this configuration. 97 | 98 | .. code:: json 99 | 100 | { 101 | "linters": { 102 | "yamllint": { 103 | "type": "script-and-regex", 104 | "script-and-regex.script": "yamllint", 105 | "script-and-regex.regex": "/^(?P\\d+):(?P\\d+) +(?Pwarning|error) +(?P.*) +\\((?P.*)\\)$/m", 106 | "include": "(\\.(yml|yaml)$)" 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /docs/quickstart.rst: -------------------------------------------------------------------------------- 1 | Quickstart 2 | ========== 3 | 4 | Installing yamllint 5 | ------------------- 6 | 7 | On Fedora / CentOS (note: `EPEL `_ is 8 | required on CentOS): 9 | 10 | .. code:: bash 11 | 12 | sudo dnf install yamllint 13 | 14 | On Debian 8+ / Ubuntu 16.04+: 15 | 16 | .. code:: bash 17 | 18 | sudo apt-get install yamllint 19 | 20 | On Mac OS 10.11+: 21 | 22 | .. code:: bash 23 | 24 | brew install yamllint 25 | 26 | On FreeBSD: 27 | 28 | .. code:: sh 29 | 30 | pkg install py36-yamllint 31 | 32 | On OpenBSD: 33 | 34 | .. code:: sh 35 | 36 | doas pkg_add py3-yamllint 37 | 38 | Alternatively using pip, the Python package manager: 39 | 40 | .. code:: bash 41 | 42 | pip install --user yamllint 43 | 44 | If you prefer installing from source, you can run, from the source directory: 45 | 46 | .. code:: bash 47 | 48 | python -m build 49 | pip install --user dist/yamllint-*.tar.gz 50 | 51 | Running yamllint 52 | ---------------- 53 | 54 | Basic usage: 55 | 56 | .. code:: bash 57 | 58 | yamllint file.yml other-file.yaml 59 | 60 | You can also lint all YAML files in a whole directory: 61 | 62 | .. code:: bash 63 | 64 | yamllint . 65 | 66 | Or lint a YAML stream from standard input: 67 | 68 | .. code:: bash 69 | 70 | echo -e 'this: is\nvalid: YAML' | yamllint - 71 | 72 | The output will look like (colors are not displayed here): 73 | 74 | :: 75 | 76 | file.yml 77 | 1:4 error trailing spaces (trailing-spaces) 78 | 4:4 error wrong indentation: expected 4 but found 3 (indentation) 79 | 5:4 error duplication of key "id-00042" in mapping (key-duplicates) 80 | 6:6 warning comment not indented like content (comments-indentation) 81 | 12:6 error too many spaces after hyphen (hyphens) 82 | 15:12 error too many spaces before comma (commas) 83 | 84 | other-file.yaml 85 | 1:1 warning missing document start "---" (document-start) 86 | 6:81 error line too long (87 > 80 characters) (line-length) 87 | 10:1 error too many blank lines (4 > 2) (empty-lines) 88 | 11:4 error too many spaces inside braces (braces) 89 | 90 | By default, the output of yamllint is colored when run from a terminal, and 91 | pure text in other cases. Add the ``-f standard`` arguments to force 92 | non-colored output. Use the ``-f colored`` arguments to force colored output. 93 | 94 | Add the ``-f parsable`` arguments if you need an output format parsable by a 95 | machine (for instance for :doc:`syntax highlighting in text editors 96 | `). The output will then look like: 97 | 98 | :: 99 | 100 | file.yml:6:2: [warning] missing starting space in comment (comments) 101 | file.yml:57:1: [error] trailing spaces (trailing-spaces) 102 | file.yml:60:3: [error] wrong indentation: expected 4 but found 2 (indentation) 103 | 104 | If you have a custom linting configuration file (see :doc:`how to configure 105 | yamllint `), it can be passed to yamllint using the ``-c`` 106 | option: 107 | 108 | .. code:: bash 109 | 110 | yamllint -c ~/myconfig file.yaml 111 | 112 | .. note:: 113 | 114 | If you have a ``.yamllint`` file in your working directory, it will be 115 | automatically loaded as configuration by yamllint. 116 | 117 | Source code 118 | ----------- 119 | 120 | The source code of yamllint is currently hosted on GitHub at 121 | `https://github.com/adrienverge/yamllint/ 122 | `_. Releases can be found `on this 123 | page `_. 124 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx-rtd-theme >=2.0.0 2 | -------------------------------------------------------------------------------- /docs/rules.rst: -------------------------------------------------------------------------------- 1 | Rules 2 | ===== 3 | 4 | When linting a document with yamllint, a series of rules (such as 5 | ``line-length``, ``trailing-spaces``, etc.) are checked against. 6 | 7 | A :doc:`configuration file ` can be used to enable or disable 8 | these rules, to set their level (*error* or *warning*), but also to tweak their 9 | options. 10 | 11 | This page describes the rules and their options. 12 | 13 | .. contents:: List of rules 14 | :local: 15 | :depth: 1 16 | 17 | anchors 18 | ------- 19 | 20 | .. automodule:: yamllint.rules.anchors 21 | 22 | braces 23 | ------ 24 | 25 | .. automodule:: yamllint.rules.braces 26 | 27 | brackets 28 | -------- 29 | 30 | .. automodule:: yamllint.rules.brackets 31 | 32 | colons 33 | ------ 34 | 35 | .. automodule:: yamllint.rules.colons 36 | 37 | commas 38 | ------ 39 | 40 | .. automodule:: yamllint.rules.commas 41 | 42 | comments 43 | -------- 44 | 45 | .. automodule:: yamllint.rules.comments 46 | 47 | comments-indentation 48 | -------------------- 49 | 50 | .. automodule:: yamllint.rules.comments_indentation 51 | 52 | document-end 53 | ------------ 54 | 55 | .. automodule:: yamllint.rules.document_end 56 | 57 | document-start 58 | -------------- 59 | 60 | .. automodule:: yamllint.rules.document_start 61 | 62 | empty-lines 63 | ----------- 64 | 65 | .. automodule:: yamllint.rules.empty_lines 66 | 67 | empty-values 68 | ------------ 69 | 70 | .. automodule:: yamllint.rules.empty_values 71 | 72 | float-values 73 | ------------ 74 | 75 | .. automodule:: yamllint.rules.float_values 76 | 77 | 78 | hyphens 79 | ------- 80 | 81 | .. automodule:: yamllint.rules.hyphens 82 | 83 | indentation 84 | ----------- 85 | 86 | .. automodule:: yamllint.rules.indentation 87 | 88 | key-duplicates 89 | -------------- 90 | 91 | .. automodule:: yamllint.rules.key_duplicates 92 | 93 | key-ordering 94 | -------------- 95 | 96 | .. automodule:: yamllint.rules.key_ordering 97 | 98 | line-length 99 | ----------- 100 | 101 | .. automodule:: yamllint.rules.line_length 102 | 103 | new-line-at-end-of-file 104 | ----------------------- 105 | 106 | .. automodule:: yamllint.rules.new_line_at_end_of_file 107 | 108 | new-lines 109 | --------- 110 | 111 | .. automodule:: yamllint.rules.new_lines 112 | 113 | octal-values 114 | ------------ 115 | 116 | .. automodule:: yamllint.rules.octal_values 117 | 118 | quoted-strings 119 | -------------- 120 | 121 | .. automodule:: yamllint.rules.quoted_strings 122 | 123 | trailing-spaces 124 | --------------- 125 | 126 | .. automodule:: yamllint.rules.trailing_spaces 127 | 128 | truthy 129 | --------------- 130 | 131 | .. automodule:: yamllint.rules.truthy 132 | -------------------------------------------------------------------------------- /docs/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adrienverge/yamllint/16430e9d0a861f03c9c6f145e789b79a66672f57/docs/screenshot.png -------------------------------------------------------------------------------- /docs/text_editors.rst: -------------------------------------------------------------------------------- 1 | Integration with text editors 2 | ============================= 3 | 4 | Most text editors support syntax checking and highlighting, to visually report 5 | syntax errors and warnings to the user. yamllint can be used to syntax-check 6 | YAML source, but a bit of configuration is required depending on your favorite 7 | text editor. 8 | 9 | Vim 10 | --- 11 | 12 | Assuming that the `ALE `_ plugin is 13 | installed, yamllint is supported by default. It is automatically enabled when 14 | editing YAML files. 15 | 16 | If you instead use the `syntastic `_ 17 | plugin, add this to your ``.vimrc``: 18 | 19 | :: 20 | 21 | let g:syntastic_yaml_checkers = ['yamllint'] 22 | 23 | Neovim 24 | ------ 25 | 26 | Assuming that the `neomake `_ plugin is 27 | installed, yamllint is supported by default. It is automatically enabled when 28 | editing YAML files. 29 | 30 | Emacs 31 | ----- 32 | 33 | If you are `flycheck `_ user, you can use 34 | `flycheck-yamllint `_ integration. 35 | 36 | Visual Studio Code 37 | ------------------ 38 | 39 | https://marketplace.visualstudio.com/items?itemName=fnando.linter 40 | 41 | IntelliJ 42 | -------- 43 | 44 | https://plugins.jetbrains.com/plugin/15349-yamllint 45 | 46 | Other text editors 47 | ------------------ 48 | 49 | .. rubric:: Help wanted! 50 | 51 | Your favorite text editor is not listed here? Help us improve by adding a 52 | section (by opening a pull-request or issue on GitHub). 53 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "yamllint" 3 | description = "A linter for YAML files." 4 | readme = {file = "README.rst", content-type = "text/x-rst"} 5 | requires-python = ">=3.9" 6 | license = {text = "GPL-3.0-or-later"} 7 | authors = [{name = "Adrien Vergé"}] 8 | keywords = ["yaml", "lint", "linter", "syntax", "checker"] 9 | classifiers = [ 10 | "Development Status :: 5 - Production/Stable", 11 | "Environment :: Console", 12 | "Intended Audience :: Developers", 13 | "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", 14 | "Programming Language :: Python", 15 | "Topic :: Software Development", 16 | "Topic :: Software Development :: Debuggers", 17 | "Topic :: Software Development :: Quality Assurance", 18 | "Topic :: Software Development :: Testing", 19 | ] 20 | dependencies = [ 21 | "pathspec >= 0.5.3", 22 | "pyyaml", 23 | ] 24 | dynamic = ["version"] 25 | 26 | [project.optional-dependencies] 27 | dev = [ 28 | "doc8", 29 | "flake8", 30 | "flake8-import-order", 31 | "rstcheck[sphinx]", 32 | "sphinx", 33 | ] 34 | 35 | [project.scripts] 36 | yamllint = "yamllint.cli:run" 37 | 38 | [project.urls] 39 | homepage = "https://github.com/adrienverge/yamllint" 40 | repository = "https://github.com/adrienverge/yamllint" 41 | documentation = "https://yamllint.readthedocs.io" 42 | 43 | [build-system] 44 | build-backend = "setuptools.build_meta" 45 | requires = ["setuptools >= 61"] 46 | 47 | [tool.setuptools] 48 | packages = ["yamllint", "yamllint.conf", "yamllint.rules"] 49 | 50 | [tool.setuptools.package-data] 51 | yamllint = ["conf/*.yaml"] 52 | 53 | [tool.setuptools.dynamic] 54 | version = {attr = "yamllint.__version__"} 55 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | from setuptools import setup 17 | 18 | # This is only kept for backward-compatibility with older versions that don't 19 | # support new packaging standards (e.g. PEP 517 or PEP 660): 20 | setup() 21 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # Copyright (C) 2025 Jason Yundt 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | 17 | import locale 18 | import os 19 | 20 | locale.setlocale(locale.LC_ALL, 'C') 21 | env_vars_that_could_interfere_with_tests = ( 22 | 'YAMLLINT_FILE_ENCODING', 23 | # yamllint uses these environment variables to find a config file. 24 | 'YAMLLINT_CONFIG_FILE', 25 | 'XDG_CONFIG_HOME', 26 | # These variables are used to determine where the user’s home 27 | # directory is. See 28 | # https://docs.python.org/3/library/os.path.html#os.path.expanduser 29 | 'HOME', 30 | 'USERPROFILE', 31 | 'HOMEPATH', 32 | 'HOMEDRIVE' 33 | ) 34 | for name in env_vars_that_could_interfere_with_tests: 35 | try: 36 | del os.environ[name] 37 | except KeyError: 38 | pass 39 | -------------------------------------------------------------------------------- /tests/rules/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adrienverge/yamllint/16430e9d0a861f03c9c6f145e789b79a66672f57/tests/rules/__init__.py -------------------------------------------------------------------------------- /tests/rules/test_comments_indentation.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | from tests.common import RuleTestCase 17 | 18 | 19 | class CommentsIndentationTestCase(RuleTestCase): 20 | rule_id = 'comments-indentation' 21 | 22 | def test_disable(self): 23 | conf = 'comments-indentation: disable' 24 | self.check('---\n' 25 | ' # line 1\n' 26 | '# line 2\n' 27 | ' # line 3\n' 28 | ' # line 4\n' 29 | '\n' 30 | 'obj:\n' 31 | ' # these\n' 32 | ' # are\n' 33 | ' # [good]\n' 34 | '# bad\n' 35 | ' # comments\n' 36 | ' a: b\n' 37 | '\n' 38 | 'obj1:\n' 39 | ' a: 1\n' 40 | ' # comments\n' 41 | '\n' 42 | 'obj2:\n' 43 | ' b: 2\n' 44 | '\n' 45 | '# empty\n' 46 | '#\n' 47 | '# comment\n' 48 | '...\n', conf) 49 | 50 | def test_enabled(self): 51 | conf = 'comments-indentation: enable' 52 | self.check('---\n' 53 | '# line 1\n' 54 | '# line 2\n', conf) 55 | self.check('---\n' 56 | ' # line 1\n' 57 | '# line 2\n', conf, problem=(2, 2)) 58 | self.check('---\n' 59 | ' # line 1\n' 60 | ' # line 2\n', conf, problem1=(2, 3)) 61 | self.check('---\n' 62 | 'obj:\n' 63 | ' # normal\n' 64 | ' a: b\n', conf) 65 | self.check('---\n' 66 | 'obj:\n' 67 | ' # bad\n' 68 | ' a: b\n', conf, problem=(3, 2)) 69 | self.check('---\n' 70 | 'obj:\n' 71 | '# bad\n' 72 | ' a: b\n', conf, problem=(3, 1)) 73 | self.check('---\n' 74 | 'obj:\n' 75 | ' # bad\n' 76 | ' a: b\n', conf, problem=(3, 4)) 77 | self.check('---\n' 78 | 'obj:\n' 79 | ' # these\n' 80 | ' # are\n' 81 | ' # [good]\n' 82 | '# bad\n' 83 | ' # comments\n' 84 | ' a: b\n', conf, 85 | problem1=(3, 2), problem2=(4, 4), 86 | problem3=(6, 1), problem4=(7, 7)) 87 | self.check('---\n' 88 | 'obj1:\n' 89 | ' a: 1\n' 90 | ' # the following line is disabled\n' 91 | ' # b: 2\n', conf) 92 | self.check('---\n' 93 | 'obj1:\n' 94 | ' a: 1\n' 95 | ' # b: 2\n' 96 | '\n' 97 | 'obj2:\n' 98 | ' b: 2\n', conf) 99 | self.check('---\n' 100 | 'obj1:\n' 101 | ' a: 1\n' 102 | ' # b: 2\n' 103 | '# this object is useless\n' 104 | 'obj2: "no"\n', conf) 105 | self.check('---\n' 106 | 'obj1:\n' 107 | ' a: 1\n' 108 | '# this object is useless\n' 109 | ' # b: 2\n' 110 | 'obj2: "no"\n', conf, problem=(5, 3)) 111 | self.check('---\n' 112 | 'obj1:\n' 113 | ' a: 1\n' 114 | ' # comments\n' 115 | ' b: 2\n', conf) 116 | self.check('---\n' 117 | 'my list for today:\n' 118 | ' - todo 1\n' 119 | ' - todo 2\n' 120 | ' # commented for now\n' 121 | ' # - todo 3\n' 122 | '...\n', conf) 123 | 124 | def test_first_line(self): 125 | conf = 'comments-indentation: enable' 126 | self.check('# comment\n', conf) 127 | self.check(' # comment\n', conf, problem=(1, 3)) 128 | 129 | def test_no_newline_at_end(self): 130 | conf = ('comments-indentation: enable\n' 131 | 'new-line-at-end-of-file: disable\n') 132 | self.check('# comment', conf) 133 | self.check(' # comment', conf, problem=(1, 3)) 134 | 135 | def test_empty_comment(self): 136 | conf = 'comments-indentation: enable' 137 | self.check('---\n' 138 | '# hey\n' 139 | '# normal\n' 140 | '#\n', conf) 141 | self.check('---\n' 142 | '# hey\n' 143 | '# normal\n' 144 | ' #\n', conf, problem=(4, 2)) 145 | 146 | def test_inline_comment(self): 147 | conf = 'comments-indentation: enable' 148 | self.check('---\n' 149 | '- a # inline\n' 150 | '# ok\n', conf) 151 | self.check('---\n' 152 | '- a # inline\n' 153 | ' # not ok\n', conf, problem=(3, 2)) 154 | self.check('---\n' 155 | ' # not ok\n' 156 | '- a # inline\n', conf, problem=(2, 2)) 157 | -------------------------------------------------------------------------------- /tests/rules/test_common.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | import unittest 17 | 18 | import yaml 19 | 20 | from yamllint.rules.common import get_line_indent 21 | 22 | 23 | class CommonTestCase(unittest.TestCase): 24 | def test_get_line_indent(self): 25 | tokens = list(yaml.scan('a: 1\n' 26 | 'b:\n' 27 | ' - c: [2, 3, {d: 4}]\n')) 28 | 29 | self.assertEqual(tokens[3].value, 'a') 30 | self.assertEqual(tokens[5].value, '1') 31 | self.assertEqual(tokens[7].value, 'b') 32 | self.assertEqual(tokens[13].value, 'c') 33 | self.assertEqual(tokens[16].value, '2') 34 | self.assertEqual(tokens[18].value, '3') 35 | self.assertEqual(tokens[22].value, 'd') 36 | self.assertEqual(tokens[24].value, '4') 37 | 38 | for i in (3, 5): 39 | self.assertEqual(get_line_indent(tokens[i]), 0) 40 | for i in (7,): 41 | self.assertEqual(get_line_indent(tokens[i]), 0) 42 | for i in (13, 16, 18, 22, 24): 43 | self.assertEqual(get_line_indent(tokens[i]), 2) 44 | -------------------------------------------------------------------------------- /tests/rules/test_document_end.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | from tests.common import RuleTestCase 17 | 18 | 19 | class DocumentEndTestCase(RuleTestCase): 20 | rule_id = 'document-end' 21 | 22 | def test_disabled(self): 23 | conf = 'document-end: disable' 24 | self.check('---\n' 25 | 'with:\n' 26 | ' document: end\n' 27 | '...\n', conf) 28 | self.check('---\n' 29 | 'without:\n' 30 | ' document: end\n', conf) 31 | 32 | def test_required(self): 33 | conf = 'document-end: {present: true}' 34 | self.check('', conf) 35 | self.check('\n', conf) 36 | self.check('---\n' 37 | 'with:\n' 38 | ' document: end\n' 39 | '...\n', conf) 40 | self.check('---\n' 41 | 'without:\n' 42 | ' document: end\n', conf, problem=(3, 1)) 43 | 44 | def test_forbidden(self): 45 | conf = 'document-end: {present: false}' 46 | self.check('---\n' 47 | 'with:\n' 48 | ' document: end\n' 49 | '...\n', conf, problem=(4, 1)) 50 | self.check('---\n' 51 | 'without:\n' 52 | ' document: end\n', conf) 53 | 54 | def test_multiple_documents(self): 55 | conf = ('document-end: {present: true}\n' 56 | 'document-start: disable\n') 57 | self.check('---\n' 58 | 'first: document\n' 59 | '...\n' 60 | '---\n' 61 | 'second: document\n' 62 | '...\n' 63 | '---\n' 64 | 'third: document\n' 65 | '...\n', conf) 66 | self.check('---\n' 67 | 'first: document\n' 68 | '...\n' 69 | '---\n' 70 | 'second: document\n' 71 | '---\n' 72 | 'third: document\n' 73 | '...\n', conf, problem=(6, 1)) 74 | 75 | def test_directives(self): 76 | conf = 'document-end: {present: true}' 77 | self.check('%YAML 1.2\n' 78 | '---\n' 79 | 'document: end\n' 80 | '...\n', conf) 81 | self.check('%YAML 1.2\n' 82 | '%TAG ! tag:clarkevans.com,2002:\n' 83 | '---\n' 84 | 'document: end\n' 85 | '...\n', conf) 86 | self.check('---\n' 87 | 'first: document\n' 88 | '...\n' 89 | '%YAML 1.2\n' 90 | '---\n' 91 | 'second: document\n' 92 | '...\n', conf) 93 | -------------------------------------------------------------------------------- /tests/rules/test_document_start.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | from tests.common import RuleTestCase 17 | 18 | 19 | class DocumentStartTestCase(RuleTestCase): 20 | rule_id = 'document-start' 21 | 22 | def test_disabled(self): 23 | conf = 'document-start: disable' 24 | self.check('', conf) 25 | self.check('key: val\n', conf) 26 | self.check('---\n' 27 | 'key: val\n', conf) 28 | 29 | def test_required(self): 30 | conf = ('document-start: {present: true}\n' 31 | 'empty-lines: disable\n') 32 | self.check('', conf) 33 | self.check('\n', conf) 34 | self.check('key: val\n', conf, problem=(1, 1)) 35 | self.check('\n' 36 | '\n' 37 | 'key: val\n', conf, problem=(3, 1)) 38 | self.check('---\n' 39 | 'key: val\n', conf) 40 | self.check('\n' 41 | '\n' 42 | '---\n' 43 | 'key: val\n', conf) 44 | 45 | def test_forbidden(self): 46 | conf = ('document-start: {present: false}\n' 47 | 'empty-lines: disable\n') 48 | self.check('', conf) 49 | self.check('key: val\n', conf) 50 | self.check('\n' 51 | '\n' 52 | 'key: val\n', conf) 53 | self.check('---\n' 54 | 'key: val\n', conf, problem=(1, 1)) 55 | self.check('\n' 56 | '\n' 57 | '---\n' 58 | 'key: val\n', conf, problem=(3, 1)) 59 | self.check('first: document\n' 60 | '---\n' 61 | 'key: val\n', conf, problem=(2, 1)) 62 | 63 | def test_multiple_documents(self): 64 | conf = 'document-start: {present: true}' 65 | self.check('---\n' 66 | 'first: document\n' 67 | '...\n' 68 | '---\n' 69 | 'second: document\n' 70 | '...\n' 71 | '---\n' 72 | 'third: document\n', conf) 73 | self.check('---\n' 74 | 'first: document\n' 75 | '---\n' 76 | 'second: document\n' 77 | '---\n' 78 | 'third: document\n', conf) 79 | self.check('---\n' 80 | 'first: document\n' 81 | '...\n' 82 | 'second: document\n' 83 | '---\n' 84 | 'third: document\n', conf, problem=(4, 1, 'syntax')) 85 | 86 | def test_directives(self): 87 | conf = 'document-start: {present: true}' 88 | self.check('%YAML 1.2\n' 89 | '---\n' 90 | 'doc: ument\n' 91 | '...\n', conf) 92 | self.check('%YAML 1.2\n' 93 | '%TAG ! tag:clarkevans.com,2002:\n' 94 | '---\n' 95 | 'doc: ument\n' 96 | '...\n', conf) 97 | self.check('---\n' 98 | 'doc: 1\n' 99 | '...\n' 100 | '%YAML 1.2\n' 101 | '---\n' 102 | 'doc: 2\n' 103 | '...\n', conf) 104 | -------------------------------------------------------------------------------- /tests/rules/test_empty_lines.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | from tests.common import RuleTestCase 17 | 18 | 19 | class EmptyLinesTestCase(RuleTestCase): 20 | rule_id = 'empty-lines' 21 | 22 | def test_disabled(self): 23 | conf = ('empty-lines: disable\n' 24 | 'new-line-at-end-of-file: disable\n' 25 | 'document-start: disable\n') 26 | self.check('', conf) 27 | self.check('\n', conf) 28 | self.check('\n\n', conf) 29 | self.check('\n\n\n\n\n\n\n\n\n', conf) 30 | self.check('some text\n\n\n\n\n\n\n\n\n', conf) 31 | self.check('\n\n\n\n\n\n\n\n\nsome text', conf) 32 | self.check('\n\n\nsome text\n\n\n', conf) 33 | 34 | def test_empty_document(self): 35 | conf = ('empty-lines: {max: 0, max-start: 0, max-end: 0}\n' 36 | 'new-line-at-end-of-file: disable\n' 37 | 'document-start: disable\n') 38 | self.check('', conf) 39 | self.check('\n', conf) 40 | 41 | def test_0_empty_lines(self): 42 | conf = ('empty-lines: {max: 0, max-start: 0, max-end: 0}\n' 43 | 'new-line-at-end-of-file: disable\n') 44 | self.check('---\n', conf) 45 | self.check('---\ntext\n\ntext', conf, problem=(3, 1)) 46 | self.check('---\ntext\n\ntext\n', conf, problem=(3, 1)) 47 | 48 | def test_10_empty_lines(self): 49 | conf = 'empty-lines: {max: 10, max-start: 0, max-end: 0}' 50 | self.check('---\nintro\n\n\n\n\n\n\n\n\n\n\nconclusion\n', conf) 51 | self.check('---\nintro\n\n\n\n\n\n\n\n\n\n\n\nconclusion\n', conf, 52 | problem=(13, 1)) 53 | 54 | def test_spaces(self): 55 | conf = ('empty-lines: {max: 1, max-start: 0, max-end: 0}\n' 56 | 'trailing-spaces: disable\n') 57 | self.check('---\nintro\n\n \n\nconclusion\n', conf) 58 | self.check('---\nintro\n\n \n\n\nconclusion\n', conf, problem=(6, 1)) 59 | 60 | def test_empty_lines_at_start(self): 61 | conf = ('empty-lines: {max: 2, max-start: 4, max-end: 0}\n' 62 | 'document-start: disable\n') 63 | self.check('\n\n\n\nnon empty\n', conf) 64 | self.check('\n\n\n\n\nnon empty\n', conf, problem=(5, 1)) 65 | 66 | conf = ('empty-lines: {max: 2, max-start: 0, max-end: 0}\n' 67 | 'document-start: disable\n') 68 | self.check('non empty\n', conf) 69 | self.check('\nnon empty\n', conf, problem=(1, 1)) 70 | 71 | def test_empty_lines_at_end(self): 72 | conf = ('empty-lines: {max: 2, max-start: 0, max-end: 4}\n' 73 | 'document-start: disable\n') 74 | self.check('non empty\n\n\n\n\n', conf) 75 | self.check('non empty\n\n\n\n\n\n', conf, problem=(6, 1)) 76 | conf = ('empty-lines: {max: 2, max-start: 0, max-end: 0}\n' 77 | 'document-start: disable\n') 78 | self.check('non empty\n', conf) 79 | self.check('non empty\n\n', conf, problem=(2, 1)) 80 | 81 | def test_with_dos_newlines(self): 82 | conf = ('empty-lines: {max: 2, max-start: 0, max-end: 0}\n' 83 | 'new-lines: {type: dos}\n' 84 | 'document-start: disable\n') 85 | self.check('---\r\n', conf) 86 | self.check('---\r\ntext\r\n\r\ntext\r\n', conf) 87 | self.check('\r\n---\r\ntext\r\n\r\ntext\r\n', conf, 88 | problem=(1, 1)) 89 | self.check('\r\n\r\n\r\n---\r\ntext\r\n\r\ntext\r\n', conf, 90 | problem=(3, 1)) 91 | self.check('---\r\ntext\r\n\r\n\r\n\r\ntext\r\n', conf, 92 | problem=(5, 1)) 93 | self.check('---\r\ntext\r\n\r\n\r\n\r\n\r\n\r\n\r\ntext\r\n', conf, 94 | problem=(8, 1)) 95 | self.check('---\r\ntext\r\n\r\ntext\r\n\r\n', conf, 96 | problem=(5, 1)) 97 | self.check('---\r\ntext\r\n\r\ntext\r\n\r\n\r\n\r\n', conf, 98 | problem=(7, 1)) 99 | -------------------------------------------------------------------------------- /tests/rules/test_float_values.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 the yamllint contributors 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | from tests.common import RuleTestCase 17 | 18 | 19 | class FloatValuesTestCase(RuleTestCase): 20 | rule_id = 'float-values' 21 | 22 | def test_disabled(self): 23 | conf = 'float-values: disable\n' 24 | self.check('---\n' 25 | '- 0.0\n' 26 | '- .NaN\n' 27 | '- .INF\n' 28 | '- .1\n' 29 | '- 10e-6\n', 30 | conf) 31 | 32 | def test_numeral_before_decimal(self): 33 | conf = ( 34 | 'float-values:\n' 35 | ' require-numeral-before-decimal: true\n' 36 | ' forbid-scientific-notation: false\n' 37 | ' forbid-nan: false\n' 38 | ' forbid-inf: false\n') 39 | self.check('---\n' 40 | '- 0.0\n' 41 | '- .1\n' 42 | '- \'.1\'\n' 43 | '- string.1\n' 44 | '- .1string\n' 45 | '- !custom_tag .2\n' 46 | '- &angle1 0.0\n' 47 | '- *angle1\n' 48 | '- &angle2 .3\n' 49 | '- *angle2\n', 50 | conf, 51 | problem1=(3, 3), 52 | problem2=(10, 11)) 53 | 54 | def test_scientific_notation(self): 55 | conf = ( 56 | 'float-values:\n' 57 | ' require-numeral-before-decimal: false\n' 58 | ' forbid-scientific-notation: true\n' 59 | ' forbid-nan: false\n' 60 | ' forbid-inf: false\n') 61 | self.check('---\n' 62 | '- 10e6\n' 63 | '- 10e-6\n' 64 | '- 0.00001\n' 65 | '- \'10e-6\'\n' 66 | '- string10e-6\n' 67 | '- 10e-6string\n' 68 | '- !custom_tag 10e-6\n' 69 | '- &angle1 0.000001\n' 70 | '- *angle1\n' 71 | '- &angle2 10e-6\n' 72 | '- *angle2\n' 73 | '- &angle3 10e6\n' 74 | '- *angle3\n', 75 | conf, 76 | problem1=(2, 3), 77 | problem2=(3, 3), 78 | problem3=(11, 11), 79 | problem4=(13, 11)) 80 | 81 | def test_nan(self): 82 | conf = ( 83 | 'float-values:\n' 84 | ' require-numeral-before-decimal: false\n' 85 | ' forbid-scientific-notation: false\n' 86 | ' forbid-nan: true\n' 87 | ' forbid-inf: false\n') 88 | self.check('---\n' 89 | '- .NaN\n' 90 | '- .NAN\n' 91 | '- \'.NaN\'\n' 92 | '- a.NaN\n' 93 | '- .NaNa\n' 94 | '- !custom_tag .NaN\n' 95 | '- &angle .nan\n' 96 | '- *angle\n', 97 | conf, 98 | problem1=(2, 3), 99 | problem2=(3, 3), 100 | problem3=(8, 10)) 101 | 102 | def test_inf(self): 103 | conf = ( 104 | 'float-values:\n' 105 | ' require-numeral-before-decimal: false\n' 106 | ' forbid-scientific-notation: false\n' 107 | ' forbid-nan: false\n' 108 | ' forbid-inf: true\n') 109 | self.check('---\n' 110 | '- .inf\n' 111 | '- .INF\n' 112 | '- -.inf\n' 113 | '- -.INF\n' 114 | '- \'.inf\'\n' 115 | '- ∞.infinity\n' 116 | '- .infinity∞\n' 117 | '- !custom_tag .inf\n' 118 | '- &angle .inf\n' 119 | '- *angle\n' 120 | '- &angle -.inf\n' 121 | '- *angle\n', 122 | conf, 123 | problem1=(2, 3), 124 | problem2=(3, 3), 125 | problem3=(4, 3), 126 | problem4=(5, 3), 127 | problem5=(10, 10), 128 | problem6=(12, 10)) 129 | -------------------------------------------------------------------------------- /tests/rules/test_hyphens.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | from tests.common import RuleTestCase 17 | 18 | 19 | class HyphenTestCase(RuleTestCase): 20 | rule_id = 'hyphens' 21 | 22 | def test_disabled(self): 23 | conf = 'hyphens: disable' 24 | self.check('---\n' 25 | '- elem1\n' 26 | '- elem2\n', conf) 27 | self.check('---\n' 28 | '- elem1\n' 29 | '- elem2\n', conf) 30 | self.check('---\n' 31 | '- elem1\n' 32 | '- elem2\n', conf) 33 | self.check('---\n' 34 | '- elem1\n' 35 | '- elem2\n', conf) 36 | self.check('---\n' 37 | 'object:\n' 38 | ' - elem1\n' 39 | ' - elem2\n', conf) 40 | self.check('---\n' 41 | 'object:\n' 42 | ' - elem1\n' 43 | ' - elem2\n', conf) 44 | self.check('---\n' 45 | 'object:\n' 46 | ' subobject:\n' 47 | ' - elem1\n' 48 | ' - elem2\n', conf) 49 | self.check('---\n' 50 | 'object:\n' 51 | ' subobject:\n' 52 | ' - elem1\n' 53 | ' - elem2\n', conf) 54 | 55 | def test_enabled(self): 56 | conf = 'hyphens: {max-spaces-after: 1}' 57 | self.check('---\n' 58 | '- elem1\n' 59 | '- elem2\n', conf) 60 | self.check('---\n' 61 | '- elem1\n' 62 | '- elem2\n', conf, problem=(3, 3)) 63 | self.check('---\n' 64 | '- elem1\n' 65 | '- elem2\n', conf, problem1=(2, 3), problem2=(3, 3)) 66 | self.check('---\n' 67 | '- elem1\n' 68 | '- elem2\n', conf, problem=(2, 3)) 69 | self.check('---\n' 70 | 'object:\n' 71 | ' - elem1\n' 72 | ' - elem2\n', conf, problem=(4, 5)) 73 | self.check('---\n' 74 | 'object:\n' 75 | ' - elem1\n' 76 | ' - elem2\n', conf, problem1=(3, 5), problem2=(4, 5)) 77 | self.check('---\n' 78 | 'object:\n' 79 | ' subobject:\n' 80 | ' - elem1\n' 81 | ' - elem2\n', conf, problem=(5, 7)) 82 | self.check('---\n' 83 | 'object:\n' 84 | ' subobject:\n' 85 | ' - elem1\n' 86 | ' - elem2\n', conf, problem1=(4, 7), problem2=(5, 7)) 87 | 88 | def test_max_3(self): 89 | conf = 'hyphens: {max-spaces-after: 3}' 90 | self.check('---\n' 91 | '- elem1\n' 92 | '- elem2\n', conf) 93 | self.check('---\n' 94 | '- elem1\n' 95 | '- elem2\n', conf, problem=(2, 5)) 96 | self.check('---\n' 97 | 'a:\n' 98 | ' b:\n' 99 | ' - elem1\n' 100 | ' - elem2\n', conf) 101 | self.check('---\n' 102 | 'a:\n' 103 | ' b:\n' 104 | ' - elem1\n' 105 | ' - elem2\n', conf, problem1=(4, 9), problem2=(5, 9)) 106 | -------------------------------------------------------------------------------- /tests/rules/test_key_ordering.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2017 Johannes F. Knauf 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | import locale 17 | 18 | from tests.common import RuleTestCase 19 | 20 | 21 | class KeyOrderingTestCase(RuleTestCase): 22 | rule_id = 'key-ordering' 23 | 24 | def test_disabled(self): 25 | conf = 'key-ordering: disable' 26 | self.check('---\n' 27 | 'block mapping:\n' 28 | ' secondkey: a\n' 29 | ' firstkey: b\n', conf) 30 | self.check('---\n' 31 | 'flow mapping:\n' 32 | ' {secondkey: a, firstkey: b}\n', conf) 33 | self.check('---\n' 34 | 'second: before_first\n' 35 | 'at: root\n', conf) 36 | self.check('---\n' 37 | 'nested but OK:\n' 38 | ' second: {first: 1}\n' 39 | ' third:\n' 40 | ' second: 2\n', conf) 41 | 42 | def test_enabled(self): 43 | conf = 'key-ordering: enable' 44 | self.check('---\n' 45 | 'block mapping:\n' 46 | ' secondkey: a\n' 47 | ' firstkey: b\n', conf, 48 | problem=(4, 3)) 49 | self.check('---\n' 50 | 'flow mapping:\n' 51 | ' {secondkey: a, firstkey: b}\n', conf, 52 | problem=(3, 18)) 53 | self.check('---\n' 54 | 'second: before_first\n' 55 | 'at: root\n', conf, 56 | problem=(3, 1)) 57 | self.check('---\n' 58 | 'nested but OK:\n' 59 | ' second: {first: 1}\n' 60 | ' third:\n' 61 | ' second: 2\n', conf) 62 | 63 | def test_word_length(self): 64 | conf = 'key-ordering: enable' 65 | self.check('---\n' 66 | 'a: 1\n' 67 | 'ab: 1\n' 68 | 'abc: 1\n', conf) 69 | self.check('---\n' 70 | 'a: 1\n' 71 | 'abc: 1\n' 72 | 'ab: 1\n', conf, 73 | problem=(4, 1)) 74 | 75 | def test_key_duplicates(self): 76 | conf = ('key-duplicates: disable\n' 77 | 'key-ordering: enable') 78 | self.check('---\n' 79 | 'key: 1\n' 80 | 'key: 2\n', conf) 81 | 82 | def test_case(self): 83 | conf = 'key-ordering: enable' 84 | self.check('---\n' 85 | 'T-shirt: 1\n' 86 | 'T-shirts: 2\n' 87 | 't-shirt: 3\n' 88 | 't-shirts: 4\n', conf) 89 | self.check('---\n' 90 | 'T-shirt: 1\n' 91 | 't-shirt: 2\n' 92 | 'T-shirts: 3\n' 93 | 't-shirts: 4\n', conf, 94 | problem=(4, 1)) 95 | 96 | def test_accents(self): 97 | conf = 'key-ordering: enable' 98 | self.check('---\n' 99 | 'hair: true\n' 100 | 'hais: true\n' 101 | 'haïr: true\n' 102 | 'haïssable: true\n', conf) 103 | self.check('---\n' 104 | 'haïr: true\n' 105 | 'hais: true\n', conf, 106 | problem=(3, 1)) 107 | 108 | def test_key_tokens_in_flow_sequences(self): 109 | conf = 'key-ordering: enable' 110 | self.check('---\n' 111 | '[\n' 112 | ' key: value, mappings, in, flow: sequence\n' 113 | ']\n', conf) 114 | 115 | def test_locale_case(self): 116 | self.addCleanup(locale.setlocale, locale.LC_ALL, (None, None)) 117 | try: 118 | locale.setlocale(locale.LC_ALL, 'en_US.UTF-8') 119 | except locale.Error: # pragma: no cover 120 | self.skipTest('locale en_US.UTF-8 not available') 121 | conf = ('key-ordering: enable') 122 | self.check('---\n' 123 | 't-shirt: 1\n' 124 | 'T-shirt: 2\n' 125 | 't-shirts: 3\n' 126 | 'T-shirts: 4\n', conf) 127 | self.check('---\n' 128 | 't-shirt: 1\n' 129 | 't-shirts: 2\n' 130 | 'T-shirt: 3\n' 131 | 'T-shirts: 4\n', conf, 132 | problem=(4, 1)) 133 | 134 | def test_locale_accents(self): 135 | self.addCleanup(locale.setlocale, locale.LC_ALL, (None, None)) 136 | try: 137 | locale.setlocale(locale.LC_ALL, 'en_US.UTF-8') 138 | except locale.Error: # pragma: no cover 139 | self.skipTest('locale en_US.UTF-8 not available') 140 | conf = ('key-ordering: enable') 141 | self.check('---\n' 142 | 'hair: true\n' 143 | 'haïr: true\n' 144 | 'hais: true\n' 145 | 'haïssable: true\n', conf) 146 | self.check('---\n' 147 | 'hais: true\n' 148 | 'haïr: true\n', conf, 149 | problem=(3, 1)) 150 | 151 | def test_ignored_keys(self): 152 | conf = ('key-ordering:\n' 153 | ' ignored-keys: ["n(a|o)me", "^b"]\n') 154 | self.check('---\n' 155 | 'a:\n' 156 | 'b:\n' 157 | 'c:\n' 158 | 'name: ignored\n' 159 | 'first-name: ignored\n' 160 | 'nome: ignored\n' 161 | 'gnomes: ignored\n' 162 | 'd:\n' 163 | 'e:\n' 164 | 'boat: ignored\n' 165 | '.boat: ERROR\n' 166 | 'call: ERROR\n' 167 | 'f:\n' 168 | 'g:\n', 169 | conf, 170 | problem1=(12, 1), 171 | problem2=(13, 1)) 172 | -------------------------------------------------------------------------------- /tests/rules/test_new_line_at_end_of_file.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | from tests.common import RuleTestCase 17 | 18 | 19 | class NewLineAtEndOfFileTestCase(RuleTestCase): 20 | rule_id = 'new-line-at-end-of-file' 21 | 22 | def test_disabled(self): 23 | conf = ('new-line-at-end-of-file: disable\n' 24 | 'empty-lines: disable\n' 25 | 'document-start: disable\n') 26 | self.check('', conf) 27 | self.check('\n', conf) 28 | self.check('word', conf) 29 | self.check('Sentence.\n', conf) 30 | 31 | def test_enabled(self): 32 | conf = ('new-line-at-end-of-file: enable\n' 33 | 'empty-lines: disable\n' 34 | 'document-start: disable\n') 35 | self.check('', conf) 36 | self.check('\n', conf) 37 | self.check('word', conf, problem=(1, 5)) 38 | self.check('Sentence.\n', conf) 39 | self.check('---\n' 40 | 'yaml: document\n' 41 | '...', conf, problem=(3, 4)) 42 | -------------------------------------------------------------------------------- /tests/rules/test_new_lines.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | from unittest import mock 17 | 18 | from tests.common import RuleTestCase 19 | 20 | 21 | class NewLinesTestCase(RuleTestCase): 22 | rule_id = 'new-lines' 23 | 24 | def test_disabled(self): 25 | conf = ('new-line-at-end-of-file: disable\n' 26 | 'new-lines: disable\n') 27 | self.check('', conf) 28 | self.check('\n', conf) 29 | self.check('\r', conf) 30 | self.check('\r\n', conf) 31 | self.check('---\ntext\n', conf) 32 | self.check('---\r\ntext\r\n', conf) 33 | 34 | def test_unix_type(self): 35 | conf = ('new-line-at-end-of-file: disable\n' 36 | 'new-lines: {type: unix}\n') 37 | self.check('', conf) 38 | self.check('\r', conf) 39 | self.check('\n', conf) 40 | self.check('\r\n', conf, problem=(1, 1)) 41 | self.check('---\ntext\n', conf) 42 | self.check('---\r\ntext\r\n', conf, problem=(1, 4)) 43 | 44 | def test_unix_type_required_st_sp(self): 45 | # If we find a CRLF when looking for Unix newlines, yamllint 46 | # should always raise, regardless of logic with 47 | # require-starting-space. 48 | conf = ('new-line-at-end-of-file: disable\n' 49 | 'new-lines: {type: unix}\n' 50 | 'comments:\n' 51 | ' require-starting-space: true\n') 52 | self.check('---\r\n#\r\n', conf, problem=(1, 4)) 53 | 54 | def test_dos_type(self): 55 | conf = ('new-line-at-end-of-file: disable\n' 56 | 'new-lines: {type: dos}\n') 57 | self.check('', conf) 58 | self.check('\r', conf) 59 | self.check('\n', conf, problem=(1, 1)) 60 | self.check('\r\n', conf) 61 | self.check('---\ntext\n', conf, problem=(1, 4)) 62 | self.check('---\r\ntext\r\n', conf) 63 | 64 | def test_platform_type(self): 65 | conf = ('new-line-at-end-of-file: disable\n' 66 | 'new-lines: {type: platform}\n') 67 | 68 | self.check('', conf) 69 | 70 | # mock the Linux new-line-character 71 | with mock.patch('yamllint.rules.new_lines.linesep', '\n'): 72 | self.check('\n', conf) 73 | self.check('\r\n', conf, problem=(1, 1)) 74 | self.check('---\ntext\n', conf) 75 | self.check('---\r\ntext\r\n', conf, problem=(1, 4)) 76 | self.check('---\r\ntext\n', conf, problem=(1, 4)) 77 | # FIXME: the following tests currently don't work 78 | # because only the first line is checked for line-endings 79 | # see: issue #475 80 | # --- 81 | # self.check('---\ntext\r\nfoo\n', conf, problem=(2, 4)) 82 | # self.check('---\ntext\r\n', conf, problem=(2, 4)) 83 | 84 | # mock the Windows new-line-character 85 | with mock.patch('yamllint.rules.new_lines.linesep', '\r\n'): 86 | self.check('\r\n', conf) 87 | self.check('\n', conf, problem=(1, 1)) 88 | self.check('---\r\ntext\r\n', conf) 89 | self.check('---\ntext\n', conf, problem=(1, 4)) 90 | self.check('---\ntext\r\n', conf, problem=(1, 4)) 91 | # FIXME: the following tests currently don't work 92 | # because only the first line is checked for line-endings 93 | # see: issue #475 94 | # --- 95 | # self.check('---\r\ntext\nfoo\r\n', conf, problem=(2, 4)) 96 | # self.check('---\r\ntext\n', conf, problem=(2, 4)) 97 | -------------------------------------------------------------------------------- /tests/rules/test_octal_values.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | from tests.common import RuleTestCase 17 | 18 | 19 | class OctalValuesTestCase(RuleTestCase): 20 | rule_id = 'octal-values' 21 | 22 | def test_disabled(self): 23 | conf = ('octal-values: disable\n' 24 | 'new-line-at-end-of-file: disable\n' 25 | 'document-start: disable\n') 26 | self.check('user-city: 010', conf) 27 | self.check('user-city: 0o10', conf) 28 | 29 | def test_implicit_octal_values(self): 30 | conf = ('octal-values:\n' 31 | ' forbid-implicit-octal: true\n' 32 | ' forbid-explicit-octal: false\n' 33 | 'new-line-at-end-of-file: disable\n' 34 | 'document-start: disable\n') 35 | self.check('after-tag: !custom_tag 010', conf) 36 | self.check('user-city: 010', conf, problem=(1, 15)) 37 | self.check('user-city: abc', conf) 38 | self.check('user-city: 010,0571', conf) 39 | self.check("user-city: '010'", conf) 40 | self.check('user-city: "010"', conf) 41 | self.check('user-city:\n' 42 | ' - 010', conf, problem=(2, 8)) 43 | self.check('user-city: [010]', conf, problem=(1, 16)) 44 | self.check('user-city: {beijing: 010}', conf, problem=(1, 25)) 45 | self.check('explicit-octal: 0o10', conf) 46 | self.check('not-number: 0abc', conf) 47 | self.check('zero: 0', conf) 48 | self.check('hex-value: 0x10', conf) 49 | self.check('number-values:\n' 50 | ' - 0.10\n' 51 | ' - .01\n' 52 | ' - 0e3\n', conf) 53 | self.check('with-decimal-digits: 012345678', conf) 54 | self.check('with-decimal-digits: 012345679', conf) 55 | 56 | def test_explicit_octal_values(self): 57 | conf = ('octal-values:\n' 58 | ' forbid-implicit-octal: false\n' 59 | ' forbid-explicit-octal: true\n' 60 | 'new-line-at-end-of-file: disable\n' 61 | 'document-start: disable\n') 62 | self.check('user-city: 0o10', conf, problem=(1, 16)) 63 | self.check('user-city: abc', conf) 64 | self.check('user-city: 0o10,0571', conf) 65 | self.check("user-city: '0o10'", conf) 66 | self.check('user-city:\n' 67 | ' - 0o10', conf, problem=(2, 9)) 68 | self.check('user-city: [0o10]', conf, problem=(1, 17)) 69 | self.check('user-city: {beijing: 0o10}', conf, problem=(1, 26)) 70 | self.check('implicit-octal: 010', conf) 71 | self.check('not-number: 0oabc', conf) 72 | self.check('zero: 0', conf) 73 | self.check('hex-value: 0x10', conf) 74 | self.check('number-values:\n' 75 | ' - 0.10\n' 76 | ' - .01\n' 77 | ' - 0e3\n', conf) 78 | self.check('user-city: "010"', conf) 79 | self.check('with-decimal-digits: 0o012345678', conf) 80 | self.check('with-decimal-digits: 0o012345679', conf) 81 | -------------------------------------------------------------------------------- /tests/rules/test_trailing_spaces.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | from tests.common import RuleTestCase 17 | 18 | 19 | class TrailingSpacesTestCase(RuleTestCase): 20 | rule_id = 'trailing-spaces' 21 | 22 | def test_disabled(self): 23 | conf = 'trailing-spaces: disable' 24 | self.check('', conf) 25 | self.check('\n', conf) 26 | self.check(' \n', conf) 27 | self.check('---\n' 28 | 'some: text \n', conf) 29 | 30 | def test_enabled(self): 31 | conf = 'trailing-spaces: enable' 32 | self.check('', conf) 33 | self.check('\n', conf) 34 | self.check(' \n', conf, problem=(1, 1)) 35 | self.check('\t\t\t\n', conf, problem=(1, 1, 'syntax')) 36 | self.check('---\n' 37 | 'some: text \n', conf, problem=(2, 11)) 38 | self.check('---\n' 39 | 'some: text\t\n', conf, problem=(2, 11, 'syntax')) 40 | 41 | def test_with_dos_new_lines(self): 42 | conf = ('trailing-spaces: enable\n' 43 | 'new-lines: {type: dos}\n') 44 | self.check('---\r\n' 45 | 'some: text\r\n', conf) 46 | self.check('---\r\n' 47 | 'some: text \r\n', conf, problem=(2, 11)) 48 | -------------------------------------------------------------------------------- /tests/test_linter.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | import io 17 | import unittest 18 | 19 | from yamllint import linter 20 | from yamllint.config import YamlLintConfig 21 | 22 | 23 | class LinterTestCase(unittest.TestCase): 24 | def fake_config(self): 25 | return YamlLintConfig('extends: default') 26 | 27 | def test_run_on_string(self): 28 | linter.run('test: document', self.fake_config()) 29 | 30 | def test_run_on_bytes(self): 31 | linter.run(b'test: document', self.fake_config()) 32 | 33 | def test_run_on_unicode(self): 34 | linter.run('test: document', self.fake_config()) 35 | 36 | def test_run_on_stream(self): 37 | linter.run(io.StringIO('hello'), self.fake_config()) 38 | 39 | def test_run_on_int(self): 40 | self.assertRaises(TypeError, linter.run, 42, self.fake_config()) 41 | 42 | def test_run_on_list(self): 43 | self.assertRaises(TypeError, linter.run, 44 | ['h', 'e', 'l', 'l', 'o'], self.fake_config()) 45 | 46 | def test_run_on_non_ascii_chars(self): 47 | s = ('- hétérogénéité\n' 48 | '# 19.99 €\n') 49 | linter.run(s, self.fake_config()) 50 | linter.run(s.encode('utf-8'), self.fake_config()) 51 | linter.run(s.encode('iso-8859-15'), self.fake_config()) 52 | 53 | s = ('- お早う御座います。\n' 54 | '# الأَبْجَدِيَّة العَرَبِيَّة\n') 55 | linter.run(s, self.fake_config()) 56 | linter.run(s.encode('utf-8'), self.fake_config()) 57 | 58 | def test_linter_problem_repr_without_rule(self): 59 | problem = linter.LintProblem(1, 2, 'problem') 60 | 61 | self.assertEqual(str(problem), '1:2: problem') 62 | 63 | def test_linter_problem_repr_with_rule(self): 64 | problem = linter.LintProblem(1, 2, 'problem', 'rule-id') 65 | 66 | self.assertEqual(str(problem), '1:2: problem (rule-id)') 67 | -------------------------------------------------------------------------------- /tests/test_module.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2017 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | import os 17 | import shutil 18 | import subprocess 19 | import sys 20 | import tempfile 21 | import unittest 22 | 23 | PYTHON = sys.executable or 'python' 24 | 25 | 26 | class ModuleTestCase(unittest.TestCase): 27 | def setUp(self): 28 | self.wd = tempfile.mkdtemp(prefix='yamllint-tests-') 29 | 30 | # file with only one warning 31 | path = os.path.join(self.wd, 'warn.yaml') 32 | with open(path, 'w', encoding='utf_8') as f: 33 | f.write('key: value\n') 34 | 35 | # file in dir 36 | os.mkdir(os.path.join(self.wd, 'sub')) 37 | path = os.path.join(self.wd, 'sub', 'nok.yaml') 38 | with open(path, 'w', encoding='utf_8') as f: 39 | f.write('---\n' 40 | 'list: [ 1, 1, 2, 3, 5, 8] \n') 41 | 42 | def tearDown(self): 43 | shutil.rmtree(self.wd) 44 | 45 | def test_run_module_no_args(self): 46 | with self.assertRaises(subprocess.CalledProcessError) as ctx: 47 | subprocess.check_output([PYTHON, '-m', 'yamllint'], 48 | stderr=subprocess.STDOUT) 49 | self.assertEqual(ctx.exception.returncode, 2) 50 | self.assertRegex(ctx.exception.output.decode(), r'^usage: yamllint') 51 | 52 | def test_run_module_on_bad_dir(self): 53 | with self.assertRaises(subprocess.CalledProcessError) as ctx: 54 | subprocess.check_output([PYTHON, '-m', 'yamllint', 55 | '/does/not/exist'], 56 | stderr=subprocess.STDOUT) 57 | self.assertRegex(ctx.exception.output.decode(), 58 | r'No such file or directory') 59 | 60 | def test_run_module_on_file(self): 61 | out = subprocess.check_output( 62 | [PYTHON, '-m', 'yamllint', os.path.join(self.wd, 'warn.yaml')]) 63 | lines = out.decode().splitlines() 64 | self.assertIn('/warn.yaml', lines[0]) 65 | self.assertEqual('\n'.join(lines[1:]), 66 | ' 1:1 warning missing document start "---"' 67 | ' (document-start)\n') 68 | 69 | def test_run_module_on_dir(self): 70 | with self.assertRaises(subprocess.CalledProcessError) as ctx: 71 | subprocess.check_output([PYTHON, '-m', 'yamllint', self.wd]) 72 | self.assertEqual(ctx.exception.returncode, 1) 73 | 74 | files = ctx.exception.output.decode().split('\n\n') 75 | self.assertIn( 76 | '/warn.yaml\n' 77 | ' 1:1 warning missing document start "---"' 78 | ' (document-start)', 79 | files[0]) 80 | self.assertIn( 81 | '/sub/nok.yaml\n' 82 | ' 2:9 error too many spaces inside brackets' 83 | ' (brackets)\n' 84 | ' 2:27 error trailing spaces (trailing-spaces)', 85 | files[1]) 86 | -------------------------------------------------------------------------------- /tests/test_syntax_errors.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | from tests.common import RuleTestCase 17 | 18 | 19 | class YamlLintTestCase(RuleTestCase): 20 | rule_id = None # syntax error 21 | 22 | def test_syntax_errors(self): 23 | self.check('---\n' 24 | 'this is not: valid: YAML\n', None, problem=(2, 19)) 25 | self.check('---\n' 26 | 'this is: valid YAML\n' 27 | '\n' 28 | 'this is an error: [\n' 29 | '\n' 30 | '...\n', None, problem=(6, 1)) 31 | self.check('%YAML 1.2\n' 32 | '%TAG ! tag:clarkevans.com,2002:\n' 33 | 'doc: ument\n' 34 | '...\n', None, problem=(3, 1)) 35 | 36 | def test_empty_flows(self): 37 | self.check('---\n' 38 | '- []\n' 39 | '- {}\n' 40 | '- [\n' 41 | ']\n' 42 | '- {\n' 43 | '}\n' 44 | '...\n', None) 45 | 46 | def test_explicit_mapping(self): 47 | self.check('---\n' 48 | '? key\n' 49 | ': - value 1\n' 50 | ' - value 2\n' 51 | '...\n', None) 52 | self.check('---\n' 53 | '?\n' 54 | ' key\n' 55 | ': {a: 1}\n' 56 | '...\n', None) 57 | self.check('---\n' 58 | '?\n' 59 | ' key\n' 60 | ':\n' 61 | ' val\n' 62 | '...\n', None) 63 | 64 | def test_mapping_between_sequences(self): 65 | # This is valid YAML. See http://www.yaml.org/spec/1.2/spec.html, 66 | # example 2.11 67 | self.check('---\n' 68 | '? - Detroit Tigers\n' 69 | ' - Chicago cubs\n' 70 | ':\n' 71 | ' - 2001-07-23\n' 72 | '\n' 73 | '? [New York Yankees,\n' 74 | ' Atlanta Braves]\n' 75 | ': [2001-07-02, 2001-08-12,\n' 76 | ' 2001-08-14]\n', None) 77 | 78 | def test_sets(self): 79 | self.check('---\n' 80 | '? key one\n' 81 | '? key two\n' 82 | '? [non, scalar, key]\n' 83 | '? key with value\n' 84 | ': value\n' 85 | '...\n', None) 86 | self.check('---\n' 87 | '? - multi\n' 88 | ' - line\n' 89 | ' - keys\n' 90 | '? in:\n' 91 | ' a:\n' 92 | ' set\n' 93 | '...\n', None) 94 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-10.1: -------------------------------------------------------------------------------- 1 | Block style: !!map 2 | Clark : Evans 3 | Ingy : döt Net 4 | Oren : Ben-Kiki 5 | 6 | Flow style: !!map { Clark: Evans, Ingy: döt Net, Oren: Ben-Kiki } 7 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-10.2: -------------------------------------------------------------------------------- 1 | Block style: !!seq 2 | - Clark Evans 3 | - Ingy döt Net 4 | - Oren Ben-Kiki 5 | 6 | Flow style: !!seq [ Clark Evans, Ingy döt Net, Oren Ben-Kiki ] 7 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-10.3: -------------------------------------------------------------------------------- 1 | Block style: !!str |- 2 | String: just a theory. 3 | 4 | Flow style: !!str "String: just a theory." 5 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-10.4: -------------------------------------------------------------------------------- 1 | !!null null: value for null key 2 | key with null value: !!null null 3 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-10.5: -------------------------------------------------------------------------------- 1 | YAML is a superset of JSON: !!bool true 2 | Pluto is a planet: !!bool false 3 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-10.6: -------------------------------------------------------------------------------- 1 | negative: !!int -12 2 | zero: !!int 0 3 | positive: !!int 34 4 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-10.7: -------------------------------------------------------------------------------- 1 | negative: !!float -1 2 | zero: !!float 0 3 | positive: !!float 2.3e4 4 | infinity: !!float .inf 5 | not a number: !!float .nan 6 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-10.8: -------------------------------------------------------------------------------- 1 | A null: null 2 | Booleans: [ true, false ] 3 | Integers: [ 0, -0, 3, -19 ] 4 | Floats: [ 0., -0.0, 12e03, -2E+05 ] 5 | Invalid: [ True, Null, 0o7, 0x3A, +12.3 ] 6 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-10.9: -------------------------------------------------------------------------------- 1 | A null: null 2 | Also a null: # Empty 3 | Not a null: "" 4 | Booleans: [ true, True, false, FALSE ] 5 | Integers: [ 0, 0o7, 0x3A, -19 ] 6 | Floats: [ 0., -0.0, .5, +12e03, -2E+05 ] 7 | Also floats: [ .inf, -.Inf, +.INF, .NAN ] 8 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-2.1: -------------------------------------------------------------------------------- 1 | - Mark McGwire 2 | - Sammy Sosa 3 | - Ken Griffey 4 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-2.10: -------------------------------------------------------------------------------- 1 | --- 2 | hr: 3 | - Mark McGwire 4 | # Following node labeled SS 5 | - &SS Sammy Sosa 6 | rbi: 7 | - *SS # Subsequent occurrence 8 | - Ken Griffey 9 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-2.11: -------------------------------------------------------------------------------- 1 | ? - Detroit Tigers 2 | - Chicago cubs 3 | : 4 | - 2001-07-23 5 | 6 | ? [ New York Yankees, 7 | Atlanta Braves ] 8 | : [ 2001-07-02, 2001-08-12, 9 | 2001-08-14 ] 10 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-2.12: -------------------------------------------------------------------------------- 1 | --- 2 | # Products purchased 3 | - item : Super Hoop 4 | quantity: 1 5 | - item : Basketball 6 | quantity: 4 7 | - item : Big Shoes 8 | quantity: 1 9 | 10 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-2.13: -------------------------------------------------------------------------------- 1 | # ASCII Art 2 | --- | 3 | \//||\/|| 4 | // || ||__ 5 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-2.14: -------------------------------------------------------------------------------- 1 | --- > 2 | Mark McGwire's 3 | year was crippled 4 | by a knee injury. 5 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-2.15: -------------------------------------------------------------------------------- 1 | > 2 | Sammy Sosa completed another 3 | fine season with great stats. 4 | 5 | 63 Home Runs 6 | 0.288 Batting Average 7 | 8 | What a year! 9 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-2.16: -------------------------------------------------------------------------------- 1 | name: Mark McGwire 2 | accomplishment: > 3 | Mark set a major league 4 | home run record in 1998. 5 | stats: | 6 | 65 Home Runs 7 | 0.278 Batting Average 8 | 9 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-2.17: -------------------------------------------------------------------------------- 1 | unicode: "Sosa did fine.\u263A" 2 | control: "\b1998\t1999\t2000\n" 3 | hex esc: "\x0d\x0a is \r\n" 4 | 5 | single: '"Howdy!" he cried.' 6 | quoted: ' # Not a ''comment''.' 7 | tie-fighter: '|\-*-/|' 8 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-2.18: -------------------------------------------------------------------------------- 1 | plain: 2 | This unquoted scalar 3 | spans many lines. 4 | 5 | quoted: "So does this 6 | quoted scalar.\n" 7 | 8 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-2.19: -------------------------------------------------------------------------------- 1 | canonical: 12345 2 | decimal: +12345 3 | octal: 0o14 4 | hexadecimal: 0xC 5 | 6 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-2.2: -------------------------------------------------------------------------------- 1 | hr: 65 # Home runs 2 | avg: 0.278 # Batting average 3 | rbi: 147 # Runs Batted In 4 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-2.20: -------------------------------------------------------------------------------- 1 | canonical: 1.23015e+3 2 | exponential: 12.3015e+02 3 | fixed: 1230.15 4 | negative infinity: -.inf 5 | not a number: .NaN 6 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-2.21: -------------------------------------------------------------------------------- 1 | null: 2 | booleans: [ true, false ] 3 | string: '012345' 4 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-2.22: -------------------------------------------------------------------------------- 1 | canonical: 2001-12-15T02:59:43.1Z 2 | iso8601: 2001-12-14t21:59:43.10-05:00 3 | spaced: 2001-12-14 21:59:43.10 -5 4 | date: 2002-12-14 5 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-2.23: -------------------------------------------------------------------------------- 1 | --- 2 | not-date: !!str 2002-04-28 3 | 4 | picture: !!binary | 5 | R0lGODlhDAAMAIQAAP//9/X 6 | 17unp5WZmZgAAAOfn515eXv 7 | Pz7Y6OjuDg4J+fn5OTk6enp 8 | 56enmleECcgggoBADs= 9 | 10 | application specific tag: !something | 11 | The semantics of the tag 12 | above may be different for 13 | different documents. 14 | 15 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-2.24: -------------------------------------------------------------------------------- 1 | %TAG ! tag:clarkevans.com,2002: 2 | --- !shape 3 | # Use the ! handle for presenting 4 | # tag:clarkevans.com,2002:circle 5 | - !circle 6 | center: &ORIGIN {x: 73, y: 129} 7 | radius: 7 8 | - !line 9 | start: *ORIGIN 10 | finish: { x: 89, y: 102 } 11 | - !label 12 | start: *ORIGIN 13 | color: 0xFFEEBB 14 | text: Pretty vector drawing. 15 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-2.25: -------------------------------------------------------------------------------- 1 | # Sets are represented as a 2 | # Mapping where each key is 3 | # associated with a null value 4 | --- !!set 5 | ? Mark McGwire 6 | ? Sammy Sosa 7 | ? Ken Griff 8 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-2.26: -------------------------------------------------------------------------------- 1 | # Ordered maps are represented as 2 | # A sequence of mappings, with 3 | # each mapping having one key 4 | --- !!omap 5 | - Mark McGwire: 65 6 | - Sammy Sosa: 63 7 | - Ken Griffy: 58 8 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-2.27: -------------------------------------------------------------------------------- 1 | --- ! 2 | invoice: 34843 3 | date : 2001-01-23 4 | bill-to: &id001 5 | given : Chris 6 | family : Dumars 7 | address: 8 | lines: | 9 | 458 Walkman Dr. 10 | Suite #292 11 | city : Royal Oak 12 | state : MI 13 | postal : 48046 14 | ship-to: *id001 15 | product: 16 | - sku : BL394D 17 | quantity : 4 18 | description : Basketball 19 | price : 450.00 20 | - sku : BL4438H 21 | quantity : 1 22 | description : Super Hoop 23 | price : 2392.00 24 | tax : 251.42 25 | total: 4443.52 26 | comments: 27 | Late afternoon is best. 28 | Backup contact is Nancy 29 | Billsmer @ 338-4338. 30 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-2.28: -------------------------------------------------------------------------------- 1 | --- 2 | Time: 2001-11-23 15:01:42 -5 3 | User: ed 4 | Warning: 5 | This is an error message 6 | for the log file 7 | --- 8 | Time: 2001-11-23 15:02:31 -5 9 | User: ed 10 | Warning: 11 | A slightly different error 12 | message. 13 | --- 14 | Date: 2001-11-23 15:03:17 -5 15 | User: ed 16 | Fatal: 17 | Unknown variable "bar" 18 | Stack: 19 | - file: TopClass.py 20 | line: 23 21 | code: | 22 | x = MoreObject("345\n") 23 | - file: MoreClass.py 24 | line: 58 25 | code: |- 26 | foo = bar 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-2.3: -------------------------------------------------------------------------------- 1 | american: 2 | - Boston Red Sox 3 | - Detroit Tigers 4 | - New York Yankees 5 | national: 6 | - New York Mets 7 | - Chicago Cubs 8 | - Atlanta Braves 9 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-2.4: -------------------------------------------------------------------------------- 1 | - 2 | name: Mark McGwire 3 | hr: 65 4 | avg: 0.278 5 | - 6 | name: Sammy Sosa 7 | hr: 63 8 | avg: 0.288 9 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-2.5: -------------------------------------------------------------------------------- 1 | - [name , hr, avg ] 2 | - [Mark McGwire, 65, 0.278] 3 | - [Sammy Sosa , 63, 0.288] 4 | 5 | 6 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-2.6: -------------------------------------------------------------------------------- 1 | Mark McGwire: {hr: 65, avg: 0.278} 2 | Sammy Sosa: { 3 | hr: 63, 4 | avg: 0.288 5 | } 6 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-2.7: -------------------------------------------------------------------------------- 1 | # Ranking of 1998 home runs 2 | --- 3 | - Mark McGwire 4 | - Sammy Sosa 5 | - Ken Griffey 6 | 7 | # Team ranking 8 | --- 9 | - Chicago Cubs 10 | - St Louis Cardinals 11 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-2.8: -------------------------------------------------------------------------------- 1 | --- 2 | time: 20:03:20 3 | player: Sammy Sosa 4 | action: strike (miss) 5 | ... 6 | --- 7 | time: 20:03:47 8 | player: Sammy Sosa 9 | action: grand slam 10 | ... 11 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-2.9: -------------------------------------------------------------------------------- 1 | --- 2 | hr: # 1998 hr ranking 3 | - Mark McGwire 4 | - Sammy Sosa 5 | rbi: 6 | # 1998 rbi ranking 7 | - Sammy Sosa 8 | - Ken Griffey 9 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-5.1: -------------------------------------------------------------------------------- 1 | # Comment only. 2 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-5.10: -------------------------------------------------------------------------------- 1 | commercial-at: @text 2 | grave-accent: `text 3 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-5.11: -------------------------------------------------------------------------------- 1 | | 2 | Line break (no glyph) 3 | Line break (glyphed) 4 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-5.12: -------------------------------------------------------------------------------- 1 | # Tabs and spaces 2 | quoted: "Quoted " 3 | block: | 4 | void main() { 5 | printf("Hello, world!\n"); 6 | } 7 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-5.13: -------------------------------------------------------------------------------- 1 | "Fun with \\ 2 | \" \a \b \e \f \ 3 | \n \r \t \v \0 \ 4 | \  \_ \N \L \P \ 5 | \x41 \u0041 \U00000041" 6 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-5.14: -------------------------------------------------------------------------------- 1 | Bad escapes: 2 | "\c 3 | \xq-" 4 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-5.2: -------------------------------------------------------------------------------- 1 | - Invalid use of BOM 2 | 3 | - Inside a document. 4 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-5.3: -------------------------------------------------------------------------------- 1 | sequence: 2 | - one 3 | - two 4 | mapping: 5 | ? sky 6 | : blue 7 | sea : green 8 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-5.4: -------------------------------------------------------------------------------- 1 | sequence: [ one, two, ] 2 | mapping: { sky: blue, sea: green } 3 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-5.5: -------------------------------------------------------------------------------- 1 | # Comment only. 2 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-5.6: -------------------------------------------------------------------------------- 1 | anchored: !local &anchor value 2 | alias: *anchor 3 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-5.7: -------------------------------------------------------------------------------- 1 | literal: | 2 | some 3 | text 4 | folded: > 5 | some 6 | text 7 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-5.8: -------------------------------------------------------------------------------- 1 | single: 'text' 2 | double: "text" 3 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-5.9: -------------------------------------------------------------------------------- 1 | %YAML 1.2 2 | --- text 3 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-6.1: -------------------------------------------------------------------------------- 1 | # Leading comment line spaces are 2 | # neither content nor indentation. 3 | 4 | Not indented: 5 | By one space: | 6 | By four 7 | spaces 8 | Flow style: [ # Leading spaces 9 | By two, # in flow style 10 | Also by two, # are neither 11 | Still by two # content nor 12 | ] # indentation. 13 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-6.10: -------------------------------------------------------------------------------- 1 | # Comment 2 | 3 | 4 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-6.11: -------------------------------------------------------------------------------- 1 | key: # Comment 2 | # lines 3 | value 4 | 5 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-6.12: -------------------------------------------------------------------------------- 1 | { first: Sammy, last: Sosa }: 2 | # Statistics: 3 | hr: # Home runs 4 | 65 5 | avg: # Average 6 | 0.278 7 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-6.13: -------------------------------------------------------------------------------- 1 | %FOO bar baz # Should be ignored 2 | # with a warning. 3 | --- "foo" 4 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-6.14: -------------------------------------------------------------------------------- 1 | %YAML 1.3 # Attempt parsing 2 | # with a warning 3 | --- 4 | "foo" 5 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-6.15: -------------------------------------------------------------------------------- 1 | %YAML 1.2 2 | %YAML 1.1 3 | foo 4 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-6.16: -------------------------------------------------------------------------------- 1 | %TAG !yaml! tag:yaml.org,2002: 2 | --- 3 | !yaml!str "foo" 4 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-6.17: -------------------------------------------------------------------------------- 1 | %TAG ! !foo 2 | %TAG ! !foo 3 | bar 4 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-6.18: -------------------------------------------------------------------------------- 1 | # Private 2 | !foo "bar" 3 | ... 4 | # Global 5 | %TAG ! tag:example.com,2000:app/ 6 | --- 7 | !foo "bar" 8 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-6.19: -------------------------------------------------------------------------------- 1 | %TAG !! tag:example.com,2000:app/ 2 | --- 3 | !!int 1 - 3 # Interval, not integer 4 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-6.2: -------------------------------------------------------------------------------- 1 | ? a 2 | : - b 3 | - - c 4 | - d 5 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-6.20: -------------------------------------------------------------------------------- 1 | %TAG !e! tag:example.com,2000:app/ 2 | --- 3 | !e!foo "bar" 4 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-6.21: -------------------------------------------------------------------------------- 1 | %TAG !m! !my- 2 | --- # Bulb here 3 | !m!light fluorescent 4 | ... 5 | %TAG !m! !my- 6 | --- # Color here 7 | !m!light green 8 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-6.22: -------------------------------------------------------------------------------- 1 | %TAG !e! tag:example.com,2000:app/ 2 | --- 3 | - !e!foo "bar" 4 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-6.23: -------------------------------------------------------------------------------- 1 | !!str &a1 "foo": 2 | !!str bar 3 | &a2 baz : *a1 4 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-6.24: -------------------------------------------------------------------------------- 1 | ! foo : 2 | ! baz 3 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-6.25: -------------------------------------------------------------------------------- 1 | - ! foo 2 | - !<$:?> bar 3 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-6.26: -------------------------------------------------------------------------------- 1 | %TAG !e! tag:example.com,2000:app/ 2 | --- 3 | - !local foo 4 | - !!str bar 5 | - !e!tag%21 baz 6 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-6.27: -------------------------------------------------------------------------------- 1 | %TAG !e! tag:example,2000:app/ 2 | --- 3 | - !e! foo 4 | - !h!bar baz 5 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-6.28: -------------------------------------------------------------------------------- 1 | # Assuming conventional resolution: 2 | - "12" 3 | - 12 4 | - ! 12 5 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-6.29: -------------------------------------------------------------------------------- 1 | First occurrence: &anchor Value 2 | Second occurrence: *anchor 3 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-6.3: -------------------------------------------------------------------------------- 1 | - foo: bar 2 | - - baz 3 | - baz 4 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-6.4: -------------------------------------------------------------------------------- 1 | plain: text 2 | lines 3 | quoted: "text 4 | lines" 5 | block: | 6 | text 7 | lines 8 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-6.5: -------------------------------------------------------------------------------- 1 | Folding: 2 | "Empty line 3 | 4 | as a line feed" 5 | Chomping: | 6 | Clipped empty lines 7 | 8 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-6.6: -------------------------------------------------------------------------------- 1 | >- 2 | trimmed 3 | 4 | 5 | 6 | as 7 | space 8 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-6.7: -------------------------------------------------------------------------------- 1 | > 2 | foo 3 | 4 | bar 5 | 6 | baz 7 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-6.8: -------------------------------------------------------------------------------- 1 | " 2 | foo 3 | 4 | bar 5 | 6 | baz 7 | " 8 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-6.9: -------------------------------------------------------------------------------- 1 | key: # Comment 2 | valueeof 3 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-7.1: -------------------------------------------------------------------------------- 1 | First occurrence: &anchor Foo 2 | Second occurrence: *anchor 3 | Override anchor: &anchor Bar 4 | Reuse anchor: *anchor 5 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-7.10: -------------------------------------------------------------------------------- 1 | # Outside flow collection: 2 | - ::vector 3 | - ": - ()" 4 | - Up, up, and away! 5 | - -123 6 | - http://example.com/foo#bar 7 | # Inside flow collection: 8 | - [ ::vector, 9 | ": - ()", 10 | "Up, up and away!", 11 | -123, 12 | http://example.com/foo#bar ] 13 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-7.11: -------------------------------------------------------------------------------- 1 | implicit block key : [ 2 | implicit flow key : value, 3 | ] 4 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-7.12: -------------------------------------------------------------------------------- 1 | 1st non-empty 2 | 3 | 2nd non-empty 4 | 3rd non-empty 5 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-7.13: -------------------------------------------------------------------------------- 1 | - [ one, two, ] 2 | - [three ,four] 3 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-7.14: -------------------------------------------------------------------------------- 1 | [ 2 | "double 3 | quoted", 'single 4 | quoted', 5 | plain 6 | text, [ nested ], 7 | single: pair, 8 | ] 9 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-7.15: -------------------------------------------------------------------------------- 1 | - { one : two , three: four , } 2 | - {five: six,seven : eight} 3 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-7.16: -------------------------------------------------------------------------------- 1 | { 2 | ? explicit: entry, 3 | implicit: entry, 4 | ? 5 | } 6 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-7.17: -------------------------------------------------------------------------------- 1 | { 2 | unquoted : "separate", 3 | http://foo.com, 4 | omitted value:, 5 | : omitted key, 6 | } 7 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-7.18: -------------------------------------------------------------------------------- 1 | { 2 | "adjacent":value, 3 | "readable": value, 4 | "empty": 5 | } 6 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-7.19: -------------------------------------------------------------------------------- 1 | [ 2 | foo: bar 3 | ] 4 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-7.2: -------------------------------------------------------------------------------- 1 | { 2 | foo : !!str, 3 | !!str : bar, 4 | } 5 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-7.20: -------------------------------------------------------------------------------- 1 | [ 2 | ? foo 3 | bar : baz 4 | ] 5 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-7.21: -------------------------------------------------------------------------------- 1 | - [ YAML : separate ] 2 | - [ : empty key entry ] 3 | - [ {JSON: like}:adjacent ] 4 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-7.22: -------------------------------------------------------------------------------- 1 | [ foo 2 | bar: invalid, 3 | "foo...>1K characters...bar": invalid ] 4 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-7.23: -------------------------------------------------------------------------------- 1 | - [ a, b ] 2 | - { a: b } 3 | - "a" 4 | - 'b' 5 | - c 6 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-7.24: -------------------------------------------------------------------------------- 1 | - !!str "a" 2 | - 'b' 3 | - &anchor "c" 4 | - *anchor 5 | - !!str 6 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-7.3: -------------------------------------------------------------------------------- 1 | { 2 | ? foo :, 3 | : bar, 4 | } 5 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-7.4: -------------------------------------------------------------------------------- 1 | "implicit block key" : [ 2 | "implicit flow key" : value, 3 | ] 4 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-7.5: -------------------------------------------------------------------------------- 1 | "folded 2 | to a space, 3 | 4 | to a line feed, or \ 5 | \ non-content" 6 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-7.6: -------------------------------------------------------------------------------- 1 | " 1st non-empty 2 | 3 | 2nd non-empty 4 | 3rd non-empty " 5 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-7.7: -------------------------------------------------------------------------------- 1 | 'here''s to "quotes"' 2 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-7.8: -------------------------------------------------------------------------------- 1 | 'implicit block key' : [ 2 | 'implicit flow key' : value, 3 | ] 4 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-7.9: -------------------------------------------------------------------------------- 1 | ' 1st non-empty 2 | 3 | 2nd non-empty 4 | 3rd non-empty ' 5 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-8.1: -------------------------------------------------------------------------------- 1 | - | # Empty header 2 | literal 3 | - >1 # Indentation indicator 4 | folded 5 | - |+ # Chomping indicator 6 | keep 7 | 8 | - >1- # Both indicators 9 | strip 10 | 11 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-8.10: -------------------------------------------------------------------------------- 1 | > 2 | 3 | folded 4 | line 5 | 6 | next 7 | line 8 | * bullet 9 | 10 | * list 11 | * lines 12 | 13 | last 14 | line 15 | 16 | # Comment 17 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-8.11: -------------------------------------------------------------------------------- 1 | > 2 | 3 | folded 4 | line 5 | 6 | next 7 | line 8 | * bullet 9 | 10 | * list 11 | * lines 12 | 13 | last 14 | line 15 | 16 | # Comment 17 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-8.12: -------------------------------------------------------------------------------- 1 | > 2 | 3 | folded 4 | line 5 | 6 | next 7 | line 8 | * bullet 9 | 10 | * list 11 | * line 12 | 13 | last 14 | line 15 | 16 | # Comment 17 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-8.13: -------------------------------------------------------------------------------- 1 | > 2 | folded 3 | line 4 | 5 | next 6 | line 7 | * bullet 8 | 9 | * list 10 | * line 11 | 12 | last 13 | line 14 | 15 | # Comment 16 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-8.14: -------------------------------------------------------------------------------- 1 | block sequence: 2 | - one 3 | - two : three 4 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-8.15: -------------------------------------------------------------------------------- 1 | - # Empty 2 | - | 3 | block node 4 | - - one # Compact 5 | - two # sequence 6 | - one: two # Compact mapping 7 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-8.16: -------------------------------------------------------------------------------- 1 | block mapping: 2 | key: value 3 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-8.17: -------------------------------------------------------------------------------- 1 | ? explicit key # Empty value 2 | ? | 3 | block key 4 | : - one # Explicit compact 5 | - two # block value 6 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-8.18: -------------------------------------------------------------------------------- 1 | plain key: in-line value 2 | : # Both empty 3 | "quoted key": 4 | - entry 5 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-8.19: -------------------------------------------------------------------------------- 1 | - sun: yellow 2 | - ? earth: blue 3 | : moon: white 4 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-8.2: -------------------------------------------------------------------------------- 1 | - | 2 | detected 3 | - > 4 | 5 | 6 | # detected 7 | - |1 8 | explicit 9 | - > 10 | 11 | detected 12 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-8.20: -------------------------------------------------------------------------------- 1 | - 2 | "flow in block" 3 | - > 4 | Block scalar 5 | - !!map # Block collection 6 | foo : bar 7 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-8.21: -------------------------------------------------------------------------------- 1 | literal: |2 2 | value 3 | folded: 4 | !foo 5 | >1 6 | value 7 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-8.22: -------------------------------------------------------------------------------- 1 | sequence: !!seq 2 | - entry 3 | - !!seq 4 | - nested 5 | mapping: !!map 6 | foo: bar 7 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-8.3: -------------------------------------------------------------------------------- 1 | - | 2 | 3 | text 4 | - > 5 | text 6 | text 7 | - |2 8 | text 9 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-8.4: -------------------------------------------------------------------------------- 1 | strip: |- 2 | text 3 | clip: | 4 | text 5 | keep: |+ 6 | text 7 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-8.5: -------------------------------------------------------------------------------- 1 | # Strip 2 | # Comments: 3 | strip: |- 4 | # text 5 | 6 | # Clip 7 | # comments: 8 | 9 | clip: | 10 | # text 11 | 12 | # Keep 13 | # comments: 14 | 15 | keep: |+ 16 | # text 17 | 18 | # Trail 19 | # comments. 20 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-8.6: -------------------------------------------------------------------------------- 1 | strip: >- 2 | 3 | clip: > 4 | 5 | keep: |+ 6 | 7 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-8.7: -------------------------------------------------------------------------------- 1 | | 2 | literal 3 | text 4 | 5 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-8.8: -------------------------------------------------------------------------------- 1 | | 2 | 3 | 4 | literal 5 | 6 | 7 | text 8 | 9 | # Comment 10 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-8.9: -------------------------------------------------------------------------------- 1 | > 2 | folded 3 | text 4 | 5 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-9.1: -------------------------------------------------------------------------------- 1 | # Comment 2 | # lines 3 | Document 4 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-9.2: -------------------------------------------------------------------------------- 1 | %YAML 1.2 2 | --- 3 | Document 4 | ... # Suffix 5 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-9.3: -------------------------------------------------------------------------------- 1 | Bare 2 | document 3 | ... 4 | # No document 5 | ... 6 | | 7 | %!PS-Adobe-2.0 # Not the first line 8 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-9.4: -------------------------------------------------------------------------------- 1 | --- 2 | { matches 3 | % : 20 } 4 | ... 5 | --- 6 | # Empty 7 | ... 8 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-9.5: -------------------------------------------------------------------------------- 1 | %YAML 1.2 2 | --- | 3 | %!PS-Adobe-2.0 4 | ... 5 | %YAML1.2 6 | --- 7 | # Empty 8 | ... 9 | -------------------------------------------------------------------------------- /tests/yaml-1.2-spec-examples/example-9.6: -------------------------------------------------------------------------------- 1 | Document 2 | --- 3 | # Empty 4 | ... 5 | %YAML 1.2 6 | --- 7 | matches %: 20 8 | -------------------------------------------------------------------------------- /yamllint/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | """A linter for YAML files. 17 | 18 | yamllint does not only check for syntax validity, but for weirdnesses like key 19 | repetition and cosmetic problems such as lines length, trailing spaces, 20 | indentation, etc.""" 21 | 22 | 23 | APP_NAME = 'yamllint' 24 | APP_VERSION = '1.37.1' 25 | APP_DESCRIPTION = __doc__ 26 | 27 | __author__ = 'Adrien Vergé' 28 | __copyright__ = 'Copyright 2022, Adrien Vergé' 29 | __license__ = 'GPLv3' 30 | __version__ = APP_VERSION 31 | -------------------------------------------------------------------------------- /yamllint/__main__.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | from yamllint.cli import run 17 | 18 | if __name__ == '__main__': 19 | run() 20 | -------------------------------------------------------------------------------- /yamllint/conf/default.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | yaml-files: 4 | - '*.yaml' 5 | - '*.yml' 6 | - '.yamllint' 7 | 8 | rules: 9 | anchors: enable 10 | braces: enable 11 | brackets: enable 12 | colons: enable 13 | commas: enable 14 | comments: 15 | level: warning 16 | comments-indentation: 17 | level: warning 18 | document-end: disable 19 | document-start: 20 | level: warning 21 | empty-lines: enable 22 | empty-values: disable 23 | float-values: disable 24 | hyphens: enable 25 | indentation: enable 26 | key-duplicates: enable 27 | key-ordering: disable 28 | line-length: enable 29 | new-line-at-end-of-file: enable 30 | new-lines: enable 31 | octal-values: disable 32 | quoted-strings: disable 33 | trailing-spaces: enable 34 | truthy: 35 | level: warning 36 | -------------------------------------------------------------------------------- /yamllint/conf/relaxed.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | extends: default 4 | 5 | rules: 6 | braces: 7 | level: warning 8 | max-spaces-inside: 1 9 | brackets: 10 | level: warning 11 | max-spaces-inside: 1 12 | colons: 13 | level: warning 14 | commas: 15 | level: warning 16 | comments: disable 17 | comments-indentation: disable 18 | document-start: disable 19 | empty-lines: 20 | level: warning 21 | hyphens: 22 | level: warning 23 | indentation: 24 | level: warning 25 | indent-sequences: consistent 26 | line-length: 27 | level: warning 28 | allow-non-breakable-inline-mappings: true 29 | truthy: disable 30 | -------------------------------------------------------------------------------- /yamllint/decoder.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023–2025 Jason Yundt 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | import codecs 17 | import os 18 | import warnings 19 | 20 | 21 | def detect_encoding(stream_data): 22 | """ 23 | Return stream_data’s character encoding 24 | 25 | Specifically, this function will take a bytes object and return a string 26 | that contains the name of one of Python’s built-in codecs [1]. 27 | 28 | The YAML spec says that streams must begin with a BOM or an ASCII 29 | character. If stream_data doesn’t begin with either of those, then this 30 | function might return the wrong encoding. See chapter 5.2 of the YAML spec 31 | for details [2]. 32 | 33 | Before this function was added, yamllint would sometimes decode text files 34 | using a non-standard character encoding. It’s possible that there are users 35 | out there who still want to use yamllint with non-standard character 36 | encodings, so this function includes an override switch for those users. If 37 | the YAMLLINT_FILE_ENCODING environment variable is set to "example_codec", 38 | then this function will always return "example_codec". 39 | 40 | [1]: 41 | [2]: 42 | """ 43 | if 'YAMLLINT_FILE_ENCODING' in os.environ: 44 | warnings.warn("YAMLLINT_FILE_ENCODING is meant for temporary " 45 | "workarounds. It may be removed in a future version of " 46 | "yamllint.") 47 | return os.environ['YAMLLINT_FILE_ENCODING'] 48 | elif stream_data.startswith(codecs.BOM_UTF32_BE): 49 | return 'utf_32' 50 | elif stream_data.startswith(b'\x00\x00\x00') and len(stream_data) >= 4: 51 | return 'utf_32_be' 52 | elif stream_data.startswith(codecs.BOM_UTF32_LE): 53 | return 'utf_32' 54 | elif stream_data[1:4] == b'\x00\x00\x00': 55 | return 'utf_32_le' 56 | elif stream_data.startswith(codecs.BOM_UTF16_BE): 57 | return 'utf_16' 58 | elif stream_data.startswith(b'\x00') and len(stream_data) >= 2: 59 | return 'utf_16_be' 60 | elif stream_data.startswith(codecs.BOM_UTF16_LE): 61 | return 'utf_16' 62 | elif stream_data[1:2] == b'\x00': 63 | return 'utf_16_le' 64 | elif stream_data.startswith(codecs.BOM_UTF8): 65 | return 'utf_8_sig' 66 | else: 67 | return 'utf_8' 68 | 69 | 70 | def auto_decode(stream_data): 71 | return stream_data.decode(encoding=detect_encoding(stream_data)) 72 | 73 | 74 | def lines_in_files(paths): 75 | """Autodecodes files and yields their lines.""" 76 | for path in paths: 77 | with open(path, 'rb') as file: 78 | text = auto_decode(file.read()) 79 | yield from text.splitlines() 80 | -------------------------------------------------------------------------------- /yamllint/parser.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | import yaml 17 | 18 | 19 | class Line: 20 | def __init__(self, line_no, buffer, start, end): 21 | self.line_no = line_no 22 | self.start = start 23 | self.end = end 24 | self.buffer = buffer 25 | 26 | @property 27 | def content(self): 28 | return self.buffer[self.start:self.end] 29 | 30 | 31 | class Token: 32 | def __init__(self, line_no, curr, prev, next, nextnext): 33 | self.line_no = line_no 34 | self.curr = curr 35 | self.prev = prev 36 | self.next = next 37 | self.nextnext = nextnext 38 | 39 | 40 | class Comment: 41 | def __init__(self, line_no, column_no, buffer, pointer, 42 | token_before=None, token_after=None, comment_before=None): 43 | self.line_no = line_no 44 | self.column_no = column_no 45 | self.buffer = buffer 46 | self.pointer = pointer 47 | self.token_before = token_before 48 | self.token_after = token_after 49 | self.comment_before = comment_before 50 | 51 | def __str__(self): 52 | end = self.buffer.find('\n', self.pointer) 53 | if end == -1: 54 | end = self.buffer.find('\0', self.pointer) 55 | if end != -1: 56 | return self.buffer[self.pointer:end] 57 | return self.buffer[self.pointer:] 58 | 59 | def __eq__(self, other): 60 | return (isinstance(other, Comment) and 61 | self.line_no == other.line_no and 62 | self.column_no == other.column_no and 63 | str(self) == str(other)) 64 | 65 | def is_inline(self): 66 | return ( 67 | not isinstance(self.token_before, yaml.StreamStartToken) and 68 | self.line_no == self.token_before.end_mark.line + 1 and 69 | # sometimes token end marks are on the next line 70 | self.buffer[self.token_before.end_mark.pointer - 1] != '\n' 71 | ) 72 | 73 | 74 | def line_generator(buffer): 75 | line_no = 1 76 | cur = 0 77 | next = buffer.find('\n') 78 | while next != -1: 79 | if next > 0 and buffer[next - 1] == '\r': 80 | yield Line(line_no, buffer, start=cur, end=next - 1) 81 | else: 82 | yield Line(line_no, buffer, start=cur, end=next) 83 | cur = next + 1 84 | next = buffer.find('\n', cur) 85 | line_no += 1 86 | 87 | yield Line(line_no, buffer, start=cur, end=len(buffer)) 88 | 89 | 90 | def comments_between_tokens(token1, token2): 91 | """Find all comments between two tokens""" 92 | if token2 is None: 93 | buf = token1.end_mark.buffer[token1.end_mark.pointer:] 94 | elif (token1.end_mark.line == token2.start_mark.line and 95 | not isinstance(token1, yaml.StreamStartToken) and 96 | not isinstance(token2, yaml.StreamEndToken)): 97 | return 98 | else: 99 | buf = token1.end_mark.buffer[token1.end_mark.pointer: 100 | token2.start_mark.pointer] 101 | 102 | line_no = token1.end_mark.line + 1 103 | column_no = token1.end_mark.column + 1 104 | pointer = token1.end_mark.pointer 105 | 106 | comment_before = None 107 | for line in buf.split('\n'): 108 | pos = line.find('#') 109 | if pos != -1: 110 | comment = Comment(line_no, column_no + pos, 111 | token1.end_mark.buffer, pointer + pos, 112 | token1, token2, comment_before) 113 | yield comment 114 | 115 | comment_before = comment 116 | 117 | pointer += len(line) + 1 118 | line_no += 1 119 | column_no = 1 120 | 121 | 122 | def token_or_comment_generator(buffer): 123 | yaml_loader = yaml.BaseLoader(buffer) 124 | 125 | try: 126 | prev = None 127 | curr = yaml_loader.get_token() 128 | while curr is not None: 129 | next = yaml_loader.get_token() 130 | nextnext = (yaml_loader.peek_token() 131 | if yaml_loader.check_token() else None) 132 | 133 | yield Token(curr.start_mark.line + 1, curr, prev, next, nextnext) 134 | 135 | yield from comments_between_tokens(curr, next) 136 | 137 | prev = curr 138 | curr = next 139 | 140 | except yaml.scanner.ScannerError: 141 | pass 142 | 143 | 144 | def token_or_comment_or_line_generator(buffer): 145 | """Generator that mixes tokens and lines, ordering them by line number""" 146 | tok_or_com_gen = token_or_comment_generator(buffer) 147 | line_gen = line_generator(buffer) 148 | 149 | tok_or_com = next(tok_or_com_gen, None) 150 | line = next(line_gen, None) 151 | 152 | while tok_or_com is not None or line is not None: 153 | if tok_or_com is None or (line is not None and 154 | tok_or_com.line_no > line.line_no): 155 | yield line 156 | line = next(line_gen, None) 157 | else: 158 | yield tok_or_com 159 | tok_or_com = next(tok_or_com_gen, None) 160 | -------------------------------------------------------------------------------- /yamllint/rules/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | from yamllint.rules import ( 17 | anchors, 18 | braces, 19 | brackets, 20 | colons, 21 | commas, 22 | comments, 23 | comments_indentation, 24 | document_end, 25 | document_start, 26 | empty_lines, 27 | empty_values, 28 | float_values, 29 | hyphens, 30 | indentation, 31 | key_duplicates, 32 | key_ordering, 33 | line_length, 34 | new_line_at_end_of_file, 35 | new_lines, 36 | octal_values, 37 | quoted_strings, 38 | trailing_spaces, 39 | truthy, 40 | ) 41 | 42 | _RULES = { 43 | anchors.ID: anchors, 44 | braces.ID: braces, 45 | brackets.ID: brackets, 46 | colons.ID: colons, 47 | commas.ID: commas, 48 | comments.ID: comments, 49 | comments_indentation.ID: comments_indentation, 50 | document_end.ID: document_end, 51 | document_start.ID: document_start, 52 | empty_lines.ID: empty_lines, 53 | empty_values.ID: empty_values, 54 | float_values.ID: float_values, 55 | hyphens.ID: hyphens, 56 | indentation.ID: indentation, 57 | key_duplicates.ID: key_duplicates, 58 | key_ordering.ID: key_ordering, 59 | line_length.ID: line_length, 60 | new_line_at_end_of_file.ID: new_line_at_end_of_file, 61 | new_lines.ID: new_lines, 62 | octal_values.ID: octal_values, 63 | quoted_strings.ID: quoted_strings, 64 | trailing_spaces.ID: trailing_spaces, 65 | truthy.ID: truthy, 66 | } 67 | 68 | 69 | def get(id): 70 | if id not in _RULES: 71 | raise ValueError(f'no such rule: "{id}"') 72 | 73 | return _RULES[id] 74 | -------------------------------------------------------------------------------- /yamllint/rules/anchors.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | """ 17 | Use this rule to report duplicated anchors and aliases referencing undeclared 18 | anchors. 19 | 20 | .. rubric:: Options 21 | 22 | * Set ``forbid-undeclared-aliases`` to ``true`` to avoid aliases that reference 23 | an anchor that hasn't been declared (either not declared at all, or declared 24 | later in the document). 25 | * Set ``forbid-duplicated-anchors`` to ``true`` to avoid duplications of a same 26 | anchor. 27 | * Set ``forbid-unused-anchors`` to ``true`` to avoid anchors being declared but 28 | not used anywhere in the YAML document via alias. 29 | 30 | .. rubric:: Default values (when enabled) 31 | 32 | .. code-block:: yaml 33 | 34 | rules: 35 | anchors: 36 | forbid-undeclared-aliases: true 37 | forbid-duplicated-anchors: false 38 | forbid-unused-anchors: false 39 | 40 | .. rubric:: Examples 41 | 42 | #. With ``anchors: {forbid-undeclared-aliases: true}`` 43 | 44 | the following code snippet would **PASS**: 45 | :: 46 | 47 | --- 48 | - &anchor 49 | foo: bar 50 | - *anchor 51 | 52 | the following code snippet would **FAIL**: 53 | :: 54 | 55 | --- 56 | - &anchor 57 | foo: bar 58 | - *unknown 59 | 60 | the following code snippet would **FAIL**: 61 | :: 62 | 63 | --- 64 | - &anchor 65 | foo: bar 66 | - <<: *unknown 67 | extra: value 68 | 69 | #. With ``anchors: {forbid-duplicated-anchors: true}`` 70 | 71 | the following code snippet would **PASS**: 72 | :: 73 | 74 | --- 75 | - &anchor1 Foo Bar 76 | - &anchor2 [item 1, item 2] 77 | 78 | the following code snippet would **FAIL**: 79 | :: 80 | 81 | --- 82 | - &anchor Foo Bar 83 | - &anchor [item 1, item 2] 84 | 85 | #. With ``anchors: {forbid-unused-anchors: true}`` 86 | 87 | the following code snippet would **PASS**: 88 | :: 89 | 90 | --- 91 | - &anchor 92 | foo: bar 93 | - *anchor 94 | 95 | the following code snippet would **FAIL**: 96 | :: 97 | 98 | --- 99 | - &anchor 100 | foo: bar 101 | - items: 102 | - item1 103 | - item2 104 | """ 105 | 106 | 107 | import yaml 108 | 109 | from yamllint.linter import LintProblem 110 | 111 | ID = 'anchors' 112 | TYPE = 'token' 113 | CONF = {'forbid-undeclared-aliases': bool, 114 | 'forbid-duplicated-anchors': bool, 115 | 'forbid-unused-anchors': bool} 116 | DEFAULT = {'forbid-undeclared-aliases': True, 117 | 'forbid-duplicated-anchors': False, 118 | 'forbid-unused-anchors': False} 119 | 120 | 121 | def check(conf, token, prev, next, nextnext, context): 122 | if (conf['forbid-undeclared-aliases'] or 123 | conf['forbid-duplicated-anchors'] or 124 | conf['forbid-unused-anchors']): 125 | if isinstance(token, ( 126 | yaml.StreamStartToken, 127 | yaml.DocumentStartToken, 128 | yaml.DocumentEndToken)): 129 | context['anchors'] = {} 130 | 131 | if (conf['forbid-undeclared-aliases'] and 132 | isinstance(token, yaml.AliasToken) and 133 | token.value not in context['anchors']): 134 | yield LintProblem( 135 | token.start_mark.line + 1, token.start_mark.column + 1, 136 | f'found undeclared alias "{token.value}"') 137 | 138 | if (conf['forbid-duplicated-anchors'] and 139 | isinstance(token, yaml.AnchorToken) and 140 | token.value in context['anchors']): 141 | yield LintProblem( 142 | token.start_mark.line + 1, token.start_mark.column + 1, 143 | f'found duplicated anchor "{token.value}"') 144 | 145 | if conf['forbid-unused-anchors']: 146 | # Unused anchors can only be detected at the end of Document. 147 | # End of document can be either 148 | # - end of stream 149 | # - end of document sign '...' 150 | # - start of a new document sign '---' 151 | # If next token indicates end of document, 152 | # check if the anchors have been used or not. 153 | # If they haven't been used, report problem on those anchors. 154 | if isinstance(next, (yaml.StreamEndToken, 155 | yaml.DocumentStartToken, 156 | yaml.DocumentEndToken)): 157 | for anchor, info in context['anchors'].items(): 158 | if not info['used']: 159 | yield LintProblem(info['line'] + 1, 160 | info['column'] + 1, 161 | f'found unused anchor "{anchor}"') 162 | elif isinstance(token, yaml.AliasToken): 163 | context['anchors'].get(token.value, {})['used'] = True 164 | 165 | if (conf['forbid-undeclared-aliases'] or 166 | conf['forbid-duplicated-anchors'] or 167 | conf['forbid-unused-anchors']): 168 | if isinstance(token, yaml.AnchorToken): 169 | context['anchors'][token.value] = { 170 | 'line': token.start_mark.line, 171 | 'column': token.start_mark.column, 172 | 'used': False 173 | } 174 | -------------------------------------------------------------------------------- /yamllint/rules/colons.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | """ 17 | Use this rule to control the number of spaces before and after colons (``:``). 18 | 19 | .. rubric:: Options 20 | 21 | * ``max-spaces-before`` defines the maximal number of spaces allowed before 22 | colons (use ``-1`` to disable). 23 | * ``max-spaces-after`` defines the maximal number of spaces allowed after 24 | colons (use ``-1`` to disable). 25 | 26 | .. rubric:: Default values (when enabled) 27 | 28 | .. code-block:: yaml 29 | 30 | rules: 31 | colons: 32 | max-spaces-before: 0 33 | max-spaces-after: 1 34 | 35 | .. rubric:: Examples 36 | 37 | #. With ``colons: {max-spaces-before: 0, max-spaces-after: 1}`` 38 | 39 | the following code snippet would **PASS**: 40 | :: 41 | 42 | object: 43 | - a 44 | - b 45 | key: value 46 | 47 | #. With ``colons: {max-spaces-before: 1}`` 48 | 49 | the following code snippet would **PASS**: 50 | :: 51 | 52 | object : 53 | - a 54 | - b 55 | 56 | the following code snippet would **FAIL**: 57 | :: 58 | 59 | object : 60 | - a 61 | - b 62 | 63 | #. With ``colons: {max-spaces-after: 2}`` 64 | 65 | the following code snippet would **PASS**: 66 | :: 67 | 68 | first: 1 69 | second: 2 70 | third: 3 71 | 72 | the following code snippet would **FAIL**: 73 | :: 74 | 75 | first: 1 76 | 2nd: 2 77 | third: 3 78 | """ 79 | 80 | 81 | import yaml 82 | 83 | from yamllint.rules.common import is_explicit_key, spaces_after, spaces_before 84 | 85 | ID = 'colons' 86 | TYPE = 'token' 87 | CONF = {'max-spaces-before': int, 88 | 'max-spaces-after': int} 89 | DEFAULT = {'max-spaces-before': 0, 90 | 'max-spaces-after': 1} 91 | 92 | 93 | def check(conf, token, prev, next, nextnext, context): 94 | if isinstance(token, yaml.ValueToken) and not ( 95 | isinstance(prev, yaml.AliasToken) and 96 | token.start_mark.pointer - prev.end_mark.pointer == 1): 97 | problem = spaces_before(token, prev, next, 98 | max=conf['max-spaces-before'], 99 | max_desc='too many spaces before colon') 100 | if problem is not None: 101 | yield problem 102 | 103 | problem = spaces_after(token, prev, next, 104 | max=conf['max-spaces-after'], 105 | max_desc='too many spaces after colon') 106 | if problem is not None: 107 | yield problem 108 | 109 | if isinstance(token, yaml.KeyToken) and is_explicit_key(token): 110 | problem = spaces_after(token, prev, next, 111 | max=conf['max-spaces-after'], 112 | max_desc='too many spaces after question mark') 113 | if problem is not None: 114 | yield problem 115 | -------------------------------------------------------------------------------- /yamllint/rules/commas.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | """ 17 | Use this rule to control the number of spaces before and after commas (``,``). 18 | 19 | .. rubric:: Options 20 | 21 | * ``max-spaces-before`` defines the maximal number of spaces allowed before 22 | commas (use ``-1`` to disable). 23 | * ``min-spaces-after`` defines the minimal number of spaces required after 24 | commas. 25 | * ``max-spaces-after`` defines the maximal number of spaces allowed after 26 | commas (use ``-1`` to disable). 27 | 28 | .. rubric:: Default values (when enabled) 29 | 30 | .. code-block:: yaml 31 | 32 | rules: 33 | commas: 34 | max-spaces-before: 0 35 | min-spaces-after: 1 36 | max-spaces-after: 1 37 | 38 | .. rubric:: Examples 39 | 40 | #. With ``commas: {max-spaces-before: 0}`` 41 | 42 | the following code snippet would **PASS**: 43 | :: 44 | 45 | strange var: 46 | [10, 20, 30, {x: 1, y: 2}] 47 | 48 | the following code snippet would **FAIL**: 49 | :: 50 | 51 | strange var: 52 | [10, 20 , 30, {x: 1, y: 2}] 53 | 54 | #. With ``commas: {max-spaces-before: 2}`` 55 | 56 | the following code snippet would **PASS**: 57 | :: 58 | 59 | strange var: 60 | [10 , 20 , 30, {x: 1 , y: 2}] 61 | 62 | #. With ``commas: {max-spaces-before: -1}`` 63 | 64 | the following code snippet would **PASS**: 65 | :: 66 | 67 | strange var: 68 | [10, 69 | 20 , 30 70 | , {x: 1, y: 2}] 71 | 72 | #. With ``commas: {min-spaces-after: 1, max-spaces-after: 1}`` 73 | 74 | the following code snippet would **PASS**: 75 | :: 76 | 77 | strange var: 78 | [10, 20, 30, {x: 1, y: 2}] 79 | 80 | the following code snippet would **FAIL**: 81 | :: 82 | 83 | strange var: 84 | [10, 20,30, {x: 1, y: 2}] 85 | 86 | #. With ``commas: {min-spaces-after: 1, max-spaces-after: 3}`` 87 | 88 | the following code snippet would **PASS**: 89 | :: 90 | 91 | strange var: 92 | [10, 20, 30, {x: 1, y: 2}] 93 | 94 | #. With ``commas: {min-spaces-after: 0, max-spaces-after: 1}`` 95 | 96 | the following code snippet would **PASS**: 97 | :: 98 | 99 | strange var: 100 | [10, 20,30, {x: 1, y: 2}] 101 | """ 102 | 103 | 104 | import yaml 105 | 106 | from yamllint.linter import LintProblem 107 | from yamllint.rules.common import spaces_after, spaces_before 108 | 109 | ID = 'commas' 110 | TYPE = 'token' 111 | CONF = {'max-spaces-before': int, 112 | 'min-spaces-after': int, 113 | 'max-spaces-after': int} 114 | DEFAULT = {'max-spaces-before': 0, 115 | 'min-spaces-after': 1, 116 | 'max-spaces-after': 1} 117 | 118 | 119 | def check(conf, token, prev, next, nextnext, context): 120 | if isinstance(token, yaml.FlowEntryToken): 121 | if (prev is not None and conf['max-spaces-before'] != -1 and 122 | prev.end_mark.line < token.start_mark.line): 123 | yield LintProblem(token.start_mark.line + 1, 124 | max(1, token.start_mark.column), 125 | 'too many spaces before comma') 126 | else: 127 | problem = spaces_before(token, prev, next, 128 | max=conf['max-spaces-before'], 129 | max_desc='too many spaces before comma') 130 | if problem is not None: 131 | yield problem 132 | 133 | problem = spaces_after(token, prev, next, 134 | min=conf['min-spaces-after'], 135 | max=conf['max-spaces-after'], 136 | min_desc='too few spaces after comma', 137 | max_desc='too many spaces after comma') 138 | if problem is not None: 139 | yield problem 140 | -------------------------------------------------------------------------------- /yamllint/rules/comments.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | """ 17 | Use this rule to control the position and formatting of comments. 18 | 19 | .. rubric:: Options 20 | 21 | * Use ``require-starting-space`` to require a space character right after the 22 | ``#``. Set to ``true`` to enable, ``false`` to disable. 23 | * Use ``ignore-shebangs`` to ignore a 24 | `shebang `_ at the beginning of 25 | the file when ``require-starting-space`` is set. 26 | * ``min-spaces-from-content`` is used to visually separate inline comments from 27 | content. It defines the minimal required number of spaces between a comment 28 | and its preceding content. 29 | 30 | .. rubric:: Default values (when enabled) 31 | 32 | .. code-block:: yaml 33 | 34 | rules: 35 | comments: 36 | require-starting-space: true 37 | ignore-shebangs: true 38 | min-spaces-from-content: 2 39 | 40 | .. rubric:: Examples 41 | 42 | #. With ``comments: {require-starting-space: true}`` 43 | 44 | the following code snippet would **PASS**: 45 | :: 46 | 47 | # This sentence 48 | # is a block comment 49 | 50 | the following code snippet would **PASS**: 51 | :: 52 | 53 | ############################## 54 | ## This is some documentation 55 | 56 | the following code snippet would **FAIL**: 57 | :: 58 | 59 | #This sentence 60 | #is a block comment 61 | 62 | #. With ``comments: {min-spaces-from-content: 2}`` 63 | 64 | the following code snippet would **PASS**: 65 | :: 66 | 67 | x = 2 ^ 127 - 1 # Mersenne prime number 68 | 69 | the following code snippet would **FAIL**: 70 | :: 71 | 72 | x = 2 ^ 127 - 1 # Mersenne prime number 73 | """ 74 | 75 | 76 | from yamllint.linter import LintProblem 77 | 78 | ID = 'comments' 79 | TYPE = 'comment' 80 | CONF = {'require-starting-space': bool, 81 | 'ignore-shebangs': bool, 82 | 'min-spaces-from-content': int} 83 | DEFAULT = {'require-starting-space': True, 84 | 'ignore-shebangs': True, 85 | 'min-spaces-from-content': 2} 86 | 87 | 88 | def check(conf, comment): 89 | if (conf['min-spaces-from-content'] != -1 and comment.is_inline() and 90 | comment.pointer - comment.token_before.end_mark.pointer < 91 | conf['min-spaces-from-content']): 92 | yield LintProblem(comment.line_no, comment.column_no, 93 | 'too few spaces before comment: expected ' 94 | f'{conf["min-spaces-from-content"]}') 95 | 96 | if conf['require-starting-space']: 97 | text_start = comment.pointer + 1 98 | while (comment.buffer[text_start] == '#' and 99 | text_start < len(comment.buffer)): 100 | text_start += 1 101 | if text_start < len(comment.buffer): 102 | if (conf['ignore-shebangs'] and 103 | comment.line_no == 1 and 104 | comment.column_no == 1 and 105 | comment.buffer[text_start] == '!'): 106 | return 107 | # We can test for both \r and \r\n just by checking first char 108 | # \r itself is a valid newline on some older OS. 109 | elif comment.buffer[text_start] not in {' ', '\n', '\r', '\x00'}: 110 | column = comment.column_no + text_start - comment.pointer 111 | yield LintProblem(comment.line_no, 112 | column, 113 | 'missing starting space in comment') 114 | -------------------------------------------------------------------------------- /yamllint/rules/comments_indentation.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | """ 17 | Use this rule to force comments to be indented like content. 18 | 19 | .. rubric:: Examples 20 | 21 | #. With ``comments-indentation: {}`` 22 | 23 | the following code snippet would **PASS**: 24 | :: 25 | 26 | # Fibonacci 27 | [0, 1, 1, 2, 3, 5] 28 | 29 | the following code snippet would **FAIL**: 30 | :: 31 | 32 | # Fibonacci 33 | [0, 1, 1, 2, 3, 5] 34 | 35 | the following code snippet would **PASS**: 36 | :: 37 | 38 | list: 39 | - 2 40 | - 3 41 | # - 4 42 | - 5 43 | 44 | the following code snippet would **FAIL**: 45 | :: 46 | 47 | list: 48 | - 2 49 | - 3 50 | # - 4 51 | - 5 52 | 53 | the following code snippet would **PASS**: 54 | :: 55 | 56 | # This is the first object 57 | obj1: 58 | - item A 59 | # - item B 60 | # This is the second object 61 | obj2: [] 62 | 63 | the following code snippet would **PASS**: 64 | :: 65 | 66 | # This sentence 67 | # is a block comment 68 | 69 | the following code snippet would **FAIL**: 70 | :: 71 | 72 | # This sentence 73 | # is a block comment 74 | """ 75 | 76 | 77 | import yaml 78 | 79 | from yamllint.linter import LintProblem 80 | from yamllint.rules.common import get_line_indent 81 | 82 | ID = 'comments-indentation' 83 | TYPE = 'comment' 84 | 85 | 86 | # Case A: 87 | # 88 | # prev: line: 89 | # # commented line 90 | # current: line 91 | # 92 | # Case B: 93 | # 94 | # prev: line 95 | # # commented line 1 96 | # # commented line 2 97 | # current: line 98 | 99 | def check(conf, comment): 100 | # Only check block comments 101 | if (not isinstance(comment.token_before, yaml.StreamStartToken) and 102 | comment.token_before.end_mark.line + 1 == comment.line_no): 103 | return 104 | 105 | next_line_indent = comment.token_after.start_mark.column 106 | if isinstance(comment.token_after, yaml.StreamEndToken): 107 | next_line_indent = 0 108 | 109 | if isinstance(comment.token_before, yaml.StreamStartToken): 110 | prev_line_indent = 0 111 | else: 112 | prev_line_indent = get_line_indent(comment.token_before) 113 | 114 | # In the following case only the next line indent is valid: 115 | # list: 116 | # # comment 117 | # - 1 118 | # - 2 119 | prev_line_indent = max(prev_line_indent, next_line_indent) 120 | 121 | # If two indents are valid but a previous comment went back to normal 122 | # indent, for the next ones to do the same. In other words, avoid this: 123 | # list: 124 | # - 1 125 | # # comment on valid indent (0) 126 | # # comment on valid indent (4) 127 | # other-list: 128 | # - 2 129 | if (comment.comment_before is not None and 130 | not comment.comment_before.is_inline()): 131 | prev_line_indent = comment.comment_before.column_no - 1 132 | 133 | if (comment.column_no - 1 != prev_line_indent and 134 | comment.column_no - 1 != next_line_indent): 135 | yield LintProblem(comment.line_no, comment.column_no, 136 | 'comment not indented like content') 137 | -------------------------------------------------------------------------------- /yamllint/rules/common.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | import string 17 | 18 | import yaml 19 | 20 | from yamllint.linter import LintProblem 21 | 22 | 23 | def spaces_after(token, prev, next, min=-1, max=-1, 24 | min_desc=None, max_desc=None): 25 | if next is not None and token.end_mark.line == next.start_mark.line: 26 | spaces = next.start_mark.pointer - token.end_mark.pointer 27 | if max != - 1 and spaces > max: 28 | return LintProblem(token.start_mark.line + 1, 29 | next.start_mark.column, max_desc) 30 | elif min != - 1 and spaces < min: 31 | return LintProblem(token.start_mark.line + 1, 32 | next.start_mark.column + 1, min_desc) 33 | 34 | 35 | def spaces_before(token, prev, next, min=-1, max=-1, 36 | min_desc=None, max_desc=None): 37 | if (prev is not None and prev.end_mark.line == token.start_mark.line and 38 | # Discard tokens (only scalars?) that end at the start of next line 39 | (prev.end_mark.pointer == 0 or 40 | prev.end_mark.buffer[prev.end_mark.pointer - 1] != '\n')): 41 | spaces = token.start_mark.pointer - prev.end_mark.pointer 42 | if max != - 1 and spaces > max: 43 | return LintProblem(token.start_mark.line + 1, 44 | token.start_mark.column, max_desc) 45 | elif min != - 1 and spaces < min: 46 | return LintProblem(token.start_mark.line + 1, 47 | token.start_mark.column + 1, min_desc) 48 | 49 | 50 | def get_line_indent(token): 51 | """Finds the indent of the line the token starts in.""" 52 | start = token.start_mark.buffer.rfind('\n', 0, 53 | token.start_mark.pointer) + 1 54 | content = start 55 | while token.start_mark.buffer[content] == ' ': 56 | content += 1 57 | return content - start 58 | 59 | 60 | def get_real_end_line(token): 61 | """Finds the line on which the token really ends. 62 | 63 | With pyyaml, scalar tokens often end on a next line. 64 | """ 65 | end_line = token.end_mark.line + 1 66 | 67 | if not isinstance(token, yaml.ScalarToken): 68 | return end_line 69 | 70 | pos = token.end_mark.pointer - 1 71 | while (pos >= token.start_mark.pointer - 1 and 72 | token.end_mark.buffer[pos] in string.whitespace): 73 | if token.end_mark.buffer[pos] == '\n': 74 | end_line -= 1 75 | pos -= 1 76 | return end_line 77 | 78 | 79 | def is_explicit_key(token): 80 | # explicit key: 81 | # ? key 82 | # : v 83 | # or 84 | # ? 85 | # key 86 | # : v 87 | return (token.start_mark.pointer < token.end_mark.pointer and 88 | token.start_mark.buffer[token.start_mark.pointer] == '?') 89 | -------------------------------------------------------------------------------- /yamllint/rules/document_end.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | """ 17 | Use this rule to require or forbid the use of document end marker (``...``). 18 | 19 | .. rubric:: Options 20 | 21 | * Set ``present`` to ``true`` when the document end marker is required, or to 22 | ``false`` when it is forbidden. 23 | 24 | .. rubric:: Default values (when enabled) 25 | 26 | .. code-block:: yaml 27 | 28 | rules: 29 | document-end: 30 | present: true 31 | 32 | .. rubric:: Examples 33 | 34 | #. With ``document-end: {present: true}`` 35 | 36 | the following code snippet would **PASS**: 37 | :: 38 | 39 | --- 40 | this: 41 | is: [a, document] 42 | ... 43 | --- 44 | - this 45 | - is: another one 46 | ... 47 | 48 | the following code snippet would **FAIL**: 49 | :: 50 | 51 | --- 52 | this: 53 | is: [a, document] 54 | --- 55 | - this 56 | - is: another one 57 | ... 58 | 59 | #. With ``document-end: {present: false}`` 60 | 61 | the following code snippet would **PASS**: 62 | :: 63 | 64 | --- 65 | this: 66 | is: [a, document] 67 | --- 68 | - this 69 | - is: another one 70 | 71 | the following code snippet would **FAIL**: 72 | :: 73 | 74 | --- 75 | this: 76 | is: [a, document] 77 | ... 78 | --- 79 | - this 80 | - is: another one 81 | """ 82 | 83 | 84 | import yaml 85 | 86 | from yamllint.linter import LintProblem 87 | 88 | ID = 'document-end' 89 | TYPE = 'token' 90 | CONF = {'present': bool} 91 | DEFAULT = {'present': True} 92 | 93 | 94 | def check(conf, token, prev, next, nextnext, context): 95 | if conf['present']: 96 | is_stream_end = isinstance(token, yaml.StreamEndToken) 97 | is_start = isinstance(token, yaml.DocumentStartToken) 98 | prev_is_end_or_stream_start = isinstance( 99 | prev, (yaml.DocumentEndToken, yaml.StreamStartToken) 100 | ) 101 | prev_is_directive = isinstance(prev, yaml.DirectiveToken) 102 | 103 | if is_stream_end and not prev_is_end_or_stream_start: 104 | yield LintProblem(token.start_mark.line, 1, 105 | 'missing document end "..."') 106 | elif is_start and not (prev_is_end_or_stream_start 107 | or prev_is_directive): 108 | yield LintProblem(token.start_mark.line + 1, 1, 109 | 'missing document end "..."') 110 | 111 | else: 112 | if isinstance(token, yaml.DocumentEndToken): 113 | yield LintProblem(token.start_mark.line + 1, 114 | token.start_mark.column + 1, 115 | 'found forbidden document end "..."') 116 | -------------------------------------------------------------------------------- /yamllint/rules/document_start.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | """ 17 | Use this rule to require or forbid the use of document start marker (``---``). 18 | 19 | .. rubric:: Options 20 | 21 | * Set ``present`` to ``true`` when the document start marker is required, or to 22 | ``false`` when it is forbidden. 23 | 24 | .. rubric:: Default values (when enabled) 25 | 26 | .. code-block:: yaml 27 | 28 | rules: 29 | document-start: 30 | present: true 31 | 32 | .. rubric:: Examples 33 | 34 | #. With ``document-start: {present: true}`` 35 | 36 | the following code snippet would **PASS**: 37 | :: 38 | 39 | --- 40 | this: 41 | is: [a, document] 42 | --- 43 | - this 44 | - is: another one 45 | 46 | the following code snippet would **FAIL**: 47 | :: 48 | 49 | this: 50 | is: [a, document] 51 | --- 52 | - this 53 | - is: another one 54 | 55 | #. With ``document-start: {present: false}`` 56 | 57 | the following code snippet would **PASS**: 58 | :: 59 | 60 | this: 61 | is: [a, document] 62 | ... 63 | 64 | the following code snippet would **FAIL**: 65 | :: 66 | 67 | --- 68 | this: 69 | is: [a, document] 70 | ... 71 | """ 72 | 73 | 74 | import yaml 75 | 76 | from yamllint.linter import LintProblem 77 | 78 | ID = 'document-start' 79 | TYPE = 'token' 80 | CONF = {'present': bool} 81 | DEFAULT = {'present': True} 82 | 83 | 84 | def check(conf, token, prev, next, nextnext, context): 85 | if conf['present']: 86 | if (isinstance(prev, (yaml.StreamStartToken, 87 | yaml.DocumentEndToken, 88 | yaml.DirectiveToken)) and 89 | not isinstance(token, (yaml.DocumentStartToken, 90 | yaml.DirectiveToken, 91 | yaml.StreamEndToken))): 92 | yield LintProblem(token.start_mark.line + 1, 1, 93 | 'missing document start "---"') 94 | 95 | else: 96 | if isinstance(token, yaml.DocumentStartToken): 97 | yield LintProblem(token.start_mark.line + 1, 98 | token.start_mark.column + 1, 99 | 'found forbidden document start "---"') 100 | -------------------------------------------------------------------------------- /yamllint/rules/empty_lines.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | """ 17 | Use this rule to set a maximal number of allowed consecutive blank lines. 18 | 19 | .. rubric:: Options 20 | 21 | * ``max`` defines the maximal number of empty lines allowed in the document. 22 | * ``max-start`` defines the maximal number of empty lines allowed at the 23 | beginning of the file. This option takes precedence over ``max``. 24 | * ``max-end`` defines the maximal number of empty lines allowed at the end of 25 | the file. This option takes precedence over ``max``. 26 | 27 | .. rubric:: Default values (when enabled) 28 | 29 | .. code-block:: yaml 30 | 31 | rules: 32 | empty-lines: 33 | max: 2 34 | max-start: 0 35 | max-end: 0 36 | 37 | .. rubric:: Examples 38 | 39 | #. With ``empty-lines: {max: 1}`` 40 | 41 | the following code snippet would **PASS**: 42 | :: 43 | 44 | - foo: 45 | - 1 46 | - 2 47 | 48 | - bar: [3, 4] 49 | 50 | the following code snippet would **FAIL**: 51 | :: 52 | 53 | - foo: 54 | - 1 55 | - 2 56 | 57 | 58 | - bar: [3, 4] 59 | """ 60 | 61 | 62 | from yamllint.linter import LintProblem 63 | 64 | ID = 'empty-lines' 65 | TYPE = 'line' 66 | CONF = {'max': int, 67 | 'max-start': int, 68 | 'max-end': int} 69 | DEFAULT = {'max': 2, 70 | 'max-start': 0, 71 | 'max-end': 0} 72 | 73 | 74 | def check(conf, line): 75 | if line.start == line.end and line.end < len(line.buffer): 76 | # Only alert on the last blank line of a series 77 | if (line.end + 2 <= len(line.buffer) and 78 | line.buffer[line.end:line.end + 2] == '\n\n'): 79 | return 80 | elif (line.end + 4 <= len(line.buffer) and 81 | line.buffer[line.end:line.end + 4] == '\r\n\r\n'): 82 | return 83 | 84 | blank_lines = 0 85 | 86 | start = line.start 87 | while start >= 2 and line.buffer[start - 2:start] == '\r\n': 88 | blank_lines += 1 89 | start -= 2 90 | while start >= 1 and line.buffer[start - 1] == '\n': 91 | blank_lines += 1 92 | start -= 1 93 | 94 | max = conf['max'] 95 | 96 | # Special case: start of document 97 | if start == 0: 98 | blank_lines += 1 # first line doesn't have a preceding \n 99 | max = conf['max-start'] 100 | 101 | # Special case: end of document 102 | # NOTE: The last line of a file is always supposed to end with a new 103 | # line. See POSIX definition of a line at: 104 | if ((line.end == len(line.buffer) - 1 and 105 | line.buffer[line.end] == '\n') or 106 | (line.end == len(line.buffer) - 2 and 107 | line.buffer[line.end:line.end + 2] == '\r\n')): 108 | # Allow the exception of the one-byte file containing '\n' 109 | if line.end == 0: 110 | return 111 | 112 | max = conf['max-end'] 113 | 114 | if blank_lines > max: 115 | yield LintProblem(line.line_no, 1, 116 | f'too many blank lines ({blank_lines} > {max})') 117 | -------------------------------------------------------------------------------- /yamllint/rules/empty_values.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2017 Greg Dubicki 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | """ 17 | Use this rule to prevent nodes with empty content, that implicitly result in 18 | ``null`` values. 19 | 20 | .. rubric:: Options 21 | 22 | * Use ``forbid-in-block-mappings`` to prevent empty values in block mappings. 23 | * Use ``forbid-in-flow-mappings`` to prevent empty values in flow mappings. 24 | * Use ``forbid-in-block-sequences`` to prevent empty values in block sequences. 25 | 26 | .. rubric:: Default values (when enabled) 27 | 28 | .. code-block:: yaml 29 | 30 | rules: 31 | empty-values: 32 | forbid-in-block-mappings: true 33 | forbid-in-flow-mappings: true 34 | forbid-in-block-sequences: true 35 | 36 | .. rubric:: Examples 37 | 38 | #. With ``empty-values: {forbid-in-block-mappings: true}`` 39 | 40 | the following code snippets would **PASS**: 41 | :: 42 | 43 | some-mapping: 44 | sub-element: correctly indented 45 | 46 | :: 47 | 48 | explicitly-null: null 49 | 50 | the following code snippets would **FAIL**: 51 | :: 52 | 53 | some-mapping: 54 | sub-element: incorrectly indented 55 | 56 | :: 57 | 58 | implicitly-null: 59 | 60 | #. With ``empty-values: {forbid-in-flow-mappings: true}`` 61 | 62 | the following code snippet would **PASS**: 63 | :: 64 | 65 | {prop: null} 66 | {a: 1, b: 2, c: 3} 67 | 68 | the following code snippets would **FAIL**: 69 | :: 70 | 71 | {prop: } 72 | 73 | :: 74 | 75 | {a: 1, b:, c: 3} 76 | 77 | #. With ``empty-values: {forbid-in-block-sequences: true}`` 78 | 79 | the following code snippet would **PASS**: 80 | :: 81 | 82 | some-sequence: 83 | - string item 84 | 85 | :: 86 | 87 | some-sequence: 88 | - null 89 | 90 | the following code snippets would **FAIL**: 91 | :: 92 | 93 | some-sequence: 94 | - 95 | 96 | :: 97 | 98 | some-sequence: 99 | - string item 100 | - 101 | 102 | """ 103 | 104 | import yaml 105 | 106 | from yamllint.linter import LintProblem 107 | 108 | ID = 'empty-values' 109 | TYPE = 'token' 110 | CONF = {'forbid-in-block-mappings': bool, 111 | 'forbid-in-flow-mappings': bool, 112 | 'forbid-in-block-sequences': bool} 113 | DEFAULT = {'forbid-in-block-mappings': True, 114 | 'forbid-in-flow-mappings': True, 115 | 'forbid-in-block-sequences': True} 116 | 117 | 118 | def check(conf, token, prev, next, nextnext, context): 119 | 120 | if conf['forbid-in-block-mappings']: 121 | if isinstance(token, yaml.ValueToken) and isinstance(next, ( 122 | yaml.KeyToken, yaml.BlockEndToken)): 123 | yield LintProblem(token.start_mark.line + 1, 124 | token.end_mark.column + 1, 125 | 'empty value in block mapping') 126 | 127 | if conf['forbid-in-flow-mappings']: 128 | if isinstance(token, yaml.ValueToken) and isinstance(next, ( 129 | yaml.FlowEntryToken, yaml.FlowMappingEndToken)): 130 | yield LintProblem(token.start_mark.line + 1, 131 | token.end_mark.column + 1, 132 | 'empty value in flow mapping') 133 | 134 | if conf['forbid-in-block-sequences']: 135 | if isinstance(token, yaml.BlockEntryToken) and isinstance(next, ( 136 | yaml.KeyToken, yaml.BlockEndToken, yaml.BlockEntryToken)): 137 | yield LintProblem(token.start_mark.line + 1, 138 | token.end_mark.column + 1, 139 | 'empty value in block sequence') 140 | -------------------------------------------------------------------------------- /yamllint/rules/float_values.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 the yamllint contributors 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | """ 17 | Use this rule to limit the permitted values for floating-point numbers. 18 | YAML permits three classes of float expressions: approximation to real numbers, 19 | positive and negative infinity and "not a number". 20 | 21 | .. rubric:: Options 22 | 23 | * Use ``require-numeral-before-decimal`` to require floats to start 24 | with a numeral (ex ``0.0`` instead of ``.0``). 25 | * Use ``forbid-scientific-notation`` to forbid scientific notation. 26 | * Use ``forbid-nan`` to forbid NaN (not a number) values. 27 | * Use ``forbid-inf`` to forbid infinite values. 28 | 29 | .. rubric:: Default values (when enabled) 30 | 31 | .. code-block:: yaml 32 | 33 | rules: 34 | float-values: 35 | forbid-inf: false 36 | forbid-nan: false 37 | forbid-scientific-notation: false 38 | require-numeral-before-decimal: false 39 | 40 | .. rubric:: Examples 41 | 42 | #. With ``float-values: {require-numeral-before-decimal: true}`` 43 | 44 | the following code snippets would **PASS**: 45 | :: 46 | 47 | anemometer: 48 | angle: 0.0 49 | 50 | the following code snippets would **FAIL**: 51 | :: 52 | 53 | anemometer: 54 | angle: .0 55 | 56 | #. With ``float-values: {forbid-scientific-notation: true}`` 57 | 58 | the following code snippets would **PASS**: 59 | :: 60 | 61 | anemometer: 62 | angle: 0.00001 63 | 64 | the following code snippets would **FAIL**: 65 | :: 66 | 67 | anemometer: 68 | angle: 10e-6 69 | 70 | #. With ``float-values: {forbid-nan: true}`` 71 | 72 | the following code snippets would **FAIL**: 73 | :: 74 | 75 | anemometer: 76 | angle: .NaN 77 | 78 | #. With ``float-values: {forbid-inf: true}`` 79 | 80 | the following code snippets would **FAIL**: 81 | :: 82 | 83 | anemometer: 84 | angle: .inf 85 | """ 86 | 87 | import re 88 | 89 | import yaml 90 | 91 | from yamllint.linter import LintProblem 92 | 93 | ID = 'float-values' 94 | TYPE = 'token' 95 | CONF = { 96 | 'require-numeral-before-decimal': bool, 97 | 'forbid-scientific-notation': bool, 98 | 'forbid-nan': bool, 99 | 'forbid-inf': bool, 100 | } 101 | DEFAULT = { 102 | 'require-numeral-before-decimal': False, 103 | 'forbid-scientific-notation': False, 104 | 'forbid-nan': False, 105 | 'forbid-inf': False, 106 | } 107 | 108 | IS_NUMERAL_BEFORE_DECIMAL_PATTERN = ( 109 | re.compile(r'[-+]?(\.[0-9]+)([eE][-+]?[0-9]+)?$') 110 | ) 111 | IS_SCIENTIFIC_NOTATION_PATTERN = re.compile( 112 | r'[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)$' 113 | ) 114 | IS_INF_PATTERN = re.compile(r'[-+]?(\.inf|\.Inf|\.INF)$') 115 | IS_NAN_PATTERN = re.compile(r'(\.nan|\.NaN|\.NAN)$') 116 | 117 | 118 | def check(conf, token, prev, next, nextnext, context): 119 | if prev and isinstance(prev, yaml.tokens.TagToken): 120 | return 121 | if not isinstance(token, yaml.tokens.ScalarToken): 122 | return 123 | if token.style: 124 | return 125 | val = token.value 126 | 127 | if conf['forbid-nan'] and IS_NAN_PATTERN.match(val): 128 | yield LintProblem( 129 | token.start_mark.line + 1, 130 | token.start_mark.column + 1, 131 | f'forbidden not a number value "{token.value}"', 132 | ) 133 | 134 | if conf['forbid-inf'] and IS_INF_PATTERN.match(val): 135 | yield LintProblem( 136 | token.start_mark.line + 1, 137 | token.start_mark.column + 1, 138 | f'forbidden infinite value "{token.value}"', 139 | ) 140 | 141 | if conf[ 142 | 'forbid-scientific-notation' 143 | ] and IS_SCIENTIFIC_NOTATION_PATTERN.match(val): 144 | yield LintProblem( 145 | token.start_mark.line + 1, 146 | token.start_mark.column + 1, 147 | f'forbidden scientific notation "{token.value}"', 148 | ) 149 | 150 | if conf[ 151 | 'require-numeral-before-decimal' 152 | ] and IS_NUMERAL_BEFORE_DECIMAL_PATTERN.match(val): 153 | yield LintProblem( 154 | token.start_mark.line + 1, 155 | token.start_mark.column + 1, 156 | f'forbidden decimal missing 0 prefix "{token.value}"', 157 | ) 158 | -------------------------------------------------------------------------------- /yamllint/rules/hyphens.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | """ 17 | Use this rule to control the number of spaces after hyphens (``-``). 18 | 19 | .. rubric:: Options 20 | 21 | * ``max-spaces-after`` defines the maximal number of spaces allowed after 22 | hyphens. 23 | 24 | .. rubric:: Default values (when enabled) 25 | 26 | .. code-block:: yaml 27 | 28 | rules: 29 | hyphens: 30 | max-spaces-after: 1 31 | 32 | .. rubric:: Examples 33 | 34 | #. With ``hyphens: {max-spaces-after: 1}`` 35 | 36 | the following code snippet would **PASS**: 37 | :: 38 | 39 | - first list: 40 | - a 41 | - b 42 | - - 1 43 | - 2 44 | - 3 45 | 46 | the following code snippet would **FAIL**: 47 | :: 48 | 49 | - first list: 50 | - a 51 | - b 52 | 53 | the following code snippet would **FAIL**: 54 | :: 55 | 56 | - - 1 57 | - 2 58 | - 3 59 | 60 | #. With ``hyphens: {max-spaces-after: 3}`` 61 | 62 | the following code snippet would **PASS**: 63 | :: 64 | 65 | - key 66 | - key2 67 | - key42 68 | 69 | the following code snippet would **FAIL**: 70 | :: 71 | 72 | - key 73 | - key2 74 | - key42 75 | """ 76 | 77 | 78 | import yaml 79 | 80 | from yamllint.rules.common import spaces_after 81 | 82 | ID = 'hyphens' 83 | TYPE = 'token' 84 | CONF = {'max-spaces-after': int} 85 | DEFAULT = {'max-spaces-after': 1} 86 | 87 | 88 | def check(conf, token, prev, next, nextnext, context): 89 | if isinstance(token, yaml.BlockEntryToken): 90 | problem = spaces_after(token, prev, next, 91 | max=conf['max-spaces-after'], 92 | max_desc='too many spaces after hyphen') 93 | if problem is not None: 94 | yield problem 95 | -------------------------------------------------------------------------------- /yamllint/rules/key_duplicates.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | """ 17 | Use this rule to prevent multiple entries with the same key in mappings. 18 | 19 | .. rubric:: Options 20 | 21 | * Use ``forbid-duplicated-merge-keys`` to forbid the usage of 22 | multiple merge keys ``<<``. 23 | 24 | .. rubric:: Default values (when enabled) 25 | 26 | .. code-block:: yaml 27 | 28 | rules: 29 | key-duplicates: 30 | forbid-duplicated-merge-keys: false 31 | 32 | .. rubric:: Examples 33 | 34 | #. With ``key-duplicates: {}`` 35 | 36 | the following code snippet would **PASS**: 37 | :: 38 | 39 | - key 1: v 40 | key 2: val 41 | key 3: value 42 | - {a: 1, b: 2, c: 3} 43 | 44 | the following code snippet would **FAIL**: 45 | :: 46 | 47 | - key 1: v 48 | key 2: val 49 | key 1: value 50 | 51 | the following code snippet would **FAIL**: 52 | :: 53 | 54 | - {a: 1, b: 2, b: 3} 55 | 56 | the following code snippet would **FAIL**: 57 | :: 58 | 59 | duplicated key: 1 60 | "duplicated key": 2 61 | 62 | other duplication: 1 63 | ? >- 64 | other 65 | duplication 66 | : 2 67 | 68 | #. With ``key-duplicates: {forbid-duplicated-merge-keys: true}`` 69 | 70 | the following code snippet would **PASS**: 71 | :: 72 | 73 | anchor_one: &anchor_one 74 | one: one 75 | anchor_two: &anchor_two 76 | two: two 77 | anchor_reference: 78 | <<: [*anchor_one, *anchor_two] 79 | 80 | the following code snippet would **FAIL**: 81 | :: 82 | 83 | anchor_one: &anchor_one 84 | one: one 85 | anchor_two: &anchor_two 86 | two: two 87 | anchor_reference: 88 | <<: *anchor_one 89 | <<: *anchor_two 90 | """ 91 | 92 | import yaml 93 | 94 | from yamllint.linter import LintProblem 95 | 96 | ID = 'key-duplicates' 97 | TYPE = 'token' 98 | CONF = {'forbid-duplicated-merge-keys': bool} 99 | DEFAULT = {'forbid-duplicated-merge-keys': False} 100 | 101 | MAP, SEQ = range(2) 102 | 103 | 104 | class Parent: 105 | def __init__(self, type): 106 | self.type = type 107 | self.keys = [] 108 | 109 | 110 | def check(conf, token, prev, next, nextnext, context): 111 | if 'stack' not in context: 112 | context['stack'] = [] 113 | 114 | if isinstance(token, (yaml.BlockMappingStartToken, 115 | yaml.FlowMappingStartToken)): 116 | context['stack'].append(Parent(MAP)) 117 | elif isinstance(token, (yaml.BlockSequenceStartToken, 118 | yaml.FlowSequenceStartToken)): 119 | context['stack'].append(Parent(SEQ)) 120 | elif isinstance(token, (yaml.BlockEndToken, 121 | yaml.FlowMappingEndToken, 122 | yaml.FlowSequenceEndToken)): 123 | if len(context['stack']) > 0: 124 | context['stack'].pop() 125 | elif (isinstance(token, yaml.KeyToken) and 126 | isinstance(next, yaml.ScalarToken)): 127 | # This check is done because KeyTokens can be found inside flow 128 | # sequences... strange, but allowed. 129 | if len(context['stack']) > 0 and context['stack'][-1].type == MAP: 130 | if (next.value in context['stack'][-1].keys and 131 | # `<<` is "merge key", see http://yaml.org/type/merge.html 132 | (next.value != '<<' or 133 | conf['forbid-duplicated-merge-keys'])): 134 | yield LintProblem( 135 | next.start_mark.line + 1, next.start_mark.column + 1, 136 | f'duplication of key "{next.value}" in mapping') 137 | else: 138 | context['stack'][-1].keys.append(next.value) 139 | -------------------------------------------------------------------------------- /yamllint/rules/key_ordering.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2017 Johannes F. Knauf 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | """ 17 | Use this rule to enforce alphabetical ordering of keys in mappings. The sorting 18 | order uses the Unicode code point number as a default. As a result, the 19 | ordering is case-sensitive and not accent-friendly (see examples below). 20 | This can be changed by setting the global ``locale`` option. This allows one 21 | to sort case and accents properly. 22 | 23 | .. rubric:: Options 24 | 25 | * ``ignored-keys`` is a list of PCRE regexes to ignore some keys while checking 26 | order, if they match any regex. 27 | 28 | .. rubric:: Default values (when enabled) 29 | 30 | .. code-block:: yaml 31 | 32 | rules: 33 | key-ordering: 34 | ignored-keys: [] 35 | 36 | .. rubric:: Examples 37 | 38 | #. With ``key-ordering: {}`` 39 | 40 | the following code snippet would **PASS**: 41 | :: 42 | 43 | - key 1: v 44 | key 2: val 45 | key 3: value 46 | - {a: 1, b: 2, c: 3} 47 | - T-shirt: 1 48 | T-shirts: 2 49 | t-shirt: 3 50 | t-shirts: 4 51 | - hair: true 52 | hais: true 53 | haïr: true 54 | haïssable: true 55 | 56 | the following code snippet would **FAIL**: 57 | :: 58 | 59 | - key 2: v 60 | key 1: val 61 | 62 | the following code snippet would **FAIL**: 63 | :: 64 | 65 | - {b: 1, a: 2} 66 | 67 | the following code snippet would **FAIL**: 68 | :: 69 | 70 | - T-shirt: 1 71 | t-shirt: 2 72 | T-shirts: 3 73 | t-shirts: 4 74 | 75 | the following code snippet would **FAIL**: 76 | :: 77 | 78 | - haïr: true 79 | hais: true 80 | 81 | #. With global option ``locale: "en_US.UTF-8"`` and rule ``key-ordering: {}`` 82 | 83 | as opposed to before, the following code snippet would now **PASS**: 84 | :: 85 | 86 | - t-shirt: 1 87 | T-shirt: 2 88 | t-shirts: 3 89 | T-shirts: 4 90 | - hair: true 91 | haïr: true 92 | hais: true 93 | haïssable: true 94 | 95 | #. With rule ``key-ordering: {ignored-keys: ["name"]}`` 96 | 97 | the following code snippet would **PASS**: 98 | :: 99 | 100 | - a: 101 | b: 102 | name: ignored 103 | first-name: ignored 104 | c: 105 | d: 106 | """ 107 | 108 | import re 109 | from locale import strcoll 110 | 111 | import yaml 112 | 113 | from yamllint.linter import LintProblem 114 | 115 | ID = 'key-ordering' 116 | TYPE = 'token' 117 | 118 | CONF = {'ignored-keys': [str]} 119 | DEFAULT = {'ignored-keys': []} 120 | MAP, SEQ = range(2) 121 | 122 | 123 | class Parent: 124 | def __init__(self, type): 125 | self.type = type 126 | self.keys = [] 127 | 128 | 129 | def check(conf, token, prev, next, nextnext, context): 130 | if 'stack' not in context: 131 | context['stack'] = [] 132 | 133 | if isinstance(token, (yaml.BlockMappingStartToken, 134 | yaml.FlowMappingStartToken)): 135 | context['stack'].append(Parent(MAP)) 136 | elif isinstance(token, (yaml.BlockSequenceStartToken, 137 | yaml.FlowSequenceStartToken)): 138 | context['stack'].append(Parent(SEQ)) 139 | elif isinstance(token, (yaml.BlockEndToken, 140 | yaml.FlowMappingEndToken, 141 | yaml.FlowSequenceEndToken)): 142 | context['stack'].pop() 143 | elif (isinstance(token, yaml.KeyToken) and 144 | isinstance(next, yaml.ScalarToken)): 145 | # This check is done because KeyTokens can be found inside flow 146 | # sequences... strange, but allowed. 147 | if (len(context['stack']) > 0 and context['stack'][-1].type == MAP and 148 | not any(re.search(r, next.value) 149 | for r in conf['ignored-keys'])): 150 | if any(strcoll(next.value, key) < 0 151 | for key in context['stack'][-1].keys): 152 | yield LintProblem( 153 | next.start_mark.line + 1, next.start_mark.column + 1, 154 | f'wrong ordering of key "{next.value}" in mapping') 155 | else: 156 | context['stack'][-1].keys.append(next.value) 157 | -------------------------------------------------------------------------------- /yamllint/rules/line_length.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | """ 17 | Use this rule to set a limit to lines length. 18 | 19 | .. rubric:: Options 20 | 21 | * ``max`` defines the maximal (inclusive) length of lines. 22 | * ``allow-non-breakable-words`` is used to allow non breakable words (without 23 | spaces inside) to overflow the limit. This is useful for long URLs, for 24 | instance. Use ``true`` to allow, ``false`` to forbid. 25 | * ``allow-non-breakable-inline-mappings`` implies ``allow-non-breakable-words`` 26 | and extends it to also allow non-breakable words in inline mappings. 27 | 28 | .. rubric:: Default values (when enabled) 29 | 30 | .. code-block:: yaml 31 | 32 | rules: 33 | line-length: 34 | max: 80 35 | allow-non-breakable-words: true 36 | allow-non-breakable-inline-mappings: false 37 | 38 | .. rubric:: Examples 39 | 40 | #. With ``line-length: {max: 70}`` 41 | 42 | the following code snippet would **PASS**: 43 | :: 44 | 45 | long sentence: 46 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do 47 | eiusmod tempor incididunt ut labore et dolore magna aliqua. 48 | 49 | the following code snippet would **FAIL**: 50 | :: 51 | 52 | long sentence: 53 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod 54 | tempor incididunt ut labore et dolore magna aliqua. 55 | 56 | #. With ``line-length: {max: 60, allow-non-breakable-words: true}`` 57 | 58 | the following code snippet would **PASS**: 59 | :: 60 | 61 | this: 62 | is: 63 | - a: 64 | http://localhost/very/very/very/very/very/very/very/very/long/url 65 | 66 | # this comment is too long, 67 | # but hard to split: 68 | # http://localhost/another/very/very/very/very/very/very/very/very/long/url 69 | 70 | the following code snippet would **FAIL**: 71 | :: 72 | 73 | - this line is waaaaaaaaaaaaaay too long but could be easily split... 74 | 75 | and the following code snippet would also **FAIL**: 76 | :: 77 | 78 | - foobar: http://localhost/very/very/very/very/very/very/very/very/long/url 79 | 80 | #. With ``line-length: {max: 60, allow-non-breakable-words: true, 81 | allow-non-breakable-inline-mappings: true}`` 82 | 83 | the following code snippet would **PASS**: 84 | :: 85 | 86 | - foobar: http://localhost/very/very/very/very/very/very/very/very/long/url 87 | 88 | #. With ``line-length: {max: 60, allow-non-breakable-words: false}`` 89 | 90 | the following code snippet would **FAIL**: 91 | :: 92 | 93 | this: 94 | is: 95 | - a: 96 | http://localhost/very/very/very/very/very/very/very/very/long/url 97 | """ 98 | 99 | 100 | import yaml 101 | 102 | from yamllint.linter import LintProblem 103 | 104 | ID = 'line-length' 105 | TYPE = 'line' 106 | CONF = {'max': int, 107 | 'allow-non-breakable-words': bool, 108 | 'allow-non-breakable-inline-mappings': bool} 109 | DEFAULT = {'max': 80, 110 | 'allow-non-breakable-words': True, 111 | 'allow-non-breakable-inline-mappings': False} 112 | 113 | 114 | def check_inline_mapping(line): 115 | loader = yaml.SafeLoader(line.content) 116 | try: 117 | while loader.peek_token(): 118 | if isinstance(loader.get_token(), yaml.BlockMappingStartToken): 119 | while loader.peek_token(): 120 | if isinstance(loader.get_token(), yaml.ValueToken): 121 | t = loader.get_token() 122 | if isinstance(t, yaml.ScalarToken): 123 | return ( 124 | ' ' not in line.content[t.start_mark.column:]) 125 | except yaml.scanner.ScannerError: 126 | pass 127 | 128 | return False 129 | 130 | 131 | def check(conf, line): 132 | if line.end - line.start > conf['max']: 133 | conf['allow-non-breakable-words'] |= \ 134 | conf['allow-non-breakable-inline-mappings'] 135 | if conf['allow-non-breakable-words']: 136 | start = line.start 137 | while start < line.end and line.buffer[start] == ' ': 138 | start += 1 139 | 140 | if start != line.end: 141 | if line.buffer[start] == '#': 142 | while line.buffer[start] == '#': 143 | start += 1 144 | start += 1 145 | elif line.buffer[start] == '-': 146 | start += 2 147 | 148 | if line.buffer.find(' ', start, line.end) == -1: 149 | return 150 | 151 | if (conf['allow-non-breakable-inline-mappings'] and 152 | check_inline_mapping(line)): 153 | return 154 | 155 | yield LintProblem(line.line_no, conf['max'] + 1, 156 | 'line too long (%d > %d characters)' % 157 | (line.end - line.start, conf['max'])) 158 | -------------------------------------------------------------------------------- /yamllint/rules/new_line_at_end_of_file.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | """ 17 | Use this rule to require a new line character (``\\n``) at the end of files. 18 | 19 | The POSIX standard `requires the last line to end with a new line character 20 | `_. 21 | All UNIX tools expect a new line at the end of files. Most text editors use 22 | this convention too. 23 | """ 24 | 25 | 26 | from yamllint.linter import LintProblem 27 | 28 | ID = 'new-line-at-end-of-file' 29 | TYPE = 'line' 30 | 31 | 32 | def check(conf, line): 33 | if line.end == len(line.buffer) and line.end > line.start: 34 | yield LintProblem(line.line_no, line.end - line.start + 1, 35 | 'no new line character at the end of file') 36 | -------------------------------------------------------------------------------- /yamllint/rules/new_lines.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | """ 17 | Use this rule to force the type of new line characters. 18 | 19 | .. rubric:: Options 20 | 21 | * Set ``type`` to ``unix`` to enforce UNIX-typed new line characters (``\\n``), 22 | set ``type`` to ``dos`` to enforce DOS-typed new line characters 23 | (``\\r\\n``), or set ``type`` to ``platform`` to infer the type from the 24 | system running yamllint (``\\n`` on POSIX / UNIX / Linux / Mac OS systems or 25 | ``\\r\\n`` on DOS / Windows systems). 26 | 27 | .. rubric:: Default values (when enabled) 28 | 29 | .. code-block:: yaml 30 | 31 | rules: 32 | new-lines: 33 | type: unix 34 | """ 35 | 36 | from os import linesep 37 | 38 | from yamllint.linter import LintProblem 39 | 40 | ID = 'new-lines' 41 | TYPE = 'line' 42 | CONF = {'type': ('unix', 'dos', 'platform')} 43 | DEFAULT = {'type': 'unix'} 44 | 45 | 46 | def check(conf, line): 47 | if conf['type'] == 'unix': 48 | newline_char = '\n' 49 | elif conf['type'] == 'platform': 50 | newline_char = linesep 51 | elif conf['type'] == 'dos': 52 | newline_char = '\r\n' 53 | 54 | if line.start == 0 and len(line.buffer) > line.end: 55 | if line.buffer[line.end:line.end + len(newline_char)] != newline_char: 56 | c = repr(newline_char).strip('\'') 57 | yield LintProblem(1, line.end - line.start + 1, 58 | f'wrong new line character: expected {c}') 59 | -------------------------------------------------------------------------------- /yamllint/rules/octal_values.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2017 ScienJus 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | """ 17 | Use this rule to prevent values with octal numbers. In YAML, numbers that 18 | start with ``0`` are interpreted as octal, but this is not always wanted. 19 | For instance ``010`` is the city code of Beijing, and should not be 20 | converted to ``8``. 21 | 22 | .. rubric:: Options 23 | 24 | * Use ``forbid-implicit-octal`` to prevent numbers starting with ``0``. 25 | * Use ``forbid-explicit-octal`` to prevent numbers starting with ``0o``. 26 | 27 | .. rubric:: Default values (when enabled) 28 | 29 | .. code-block:: yaml 30 | 31 | rules: 32 | octal-values: 33 | forbid-implicit-octal: true 34 | forbid-explicit-octal: true 35 | 36 | .. rubric:: Examples 37 | 38 | #. With ``octal-values: {forbid-implicit-octal: true}`` 39 | 40 | the following code snippets would **PASS**: 41 | :: 42 | 43 | user: 44 | city-code: '010' 45 | 46 | the following code snippets would **PASS**: 47 | :: 48 | 49 | user: 50 | city-code: 010,021 51 | 52 | the following code snippets would **FAIL**: 53 | :: 54 | 55 | user: 56 | city-code: 010 57 | 58 | #. With ``octal-values: {forbid-explicit-octal: true}`` 59 | 60 | the following code snippets would **PASS**: 61 | :: 62 | 63 | user: 64 | city-code: '0o10' 65 | 66 | the following code snippets would **FAIL**: 67 | :: 68 | 69 | user: 70 | city-code: 0o10 71 | """ 72 | 73 | import re 74 | 75 | import yaml 76 | 77 | from yamllint.linter import LintProblem 78 | 79 | ID = 'octal-values' 80 | TYPE = 'token' 81 | CONF = {'forbid-implicit-octal': bool, 82 | 'forbid-explicit-octal': bool} 83 | DEFAULT = {'forbid-implicit-octal': True, 84 | 'forbid-explicit-octal': True} 85 | 86 | IS_OCTAL_NUMBER_PATTERN = re.compile(r'^[0-7]+$') 87 | 88 | 89 | def check(conf, token, prev, next, nextnext, context): 90 | if prev and isinstance(prev, yaml.tokens.TagToken): 91 | return 92 | 93 | if conf['forbid-implicit-octal']: 94 | if isinstance(token, yaml.tokens.ScalarToken): 95 | if not token.style: 96 | val = token.value 97 | if (val.isdigit() and len(val) > 1 and val[0] == '0' and 98 | IS_OCTAL_NUMBER_PATTERN.match(val[1:])): 99 | yield LintProblem( 100 | token.start_mark.line + 1, token.end_mark.column + 1, 101 | f'forbidden implicit octal value "{token.value}"') 102 | 103 | if conf['forbid-explicit-octal']: 104 | if isinstance(token, yaml.tokens.ScalarToken): 105 | if not token.style: 106 | val = token.value 107 | if (len(val) > 2 and val[:2] == '0o' and 108 | IS_OCTAL_NUMBER_PATTERN.match(val[2:])): 109 | yield LintProblem( 110 | token.start_mark.line + 1, token.end_mark.column + 1, 111 | f'forbidden explicit octal value "{token.value}"') 112 | -------------------------------------------------------------------------------- /yamllint/rules/trailing_spaces.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Adrien Vergé 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | """ 17 | Use this rule to forbid trailing spaces at the end of lines. 18 | 19 | .. rubric:: Examples 20 | 21 | #. With ``trailing-spaces: {}`` 22 | 23 | the following code snippet would **PASS**: 24 | :: 25 | 26 | this document doesn't contain 27 | any trailing 28 | spaces 29 | 30 | the following code snippet would **FAIL**: 31 | :: 32 | 33 | this document contains """ """ 34 | trailing spaces 35 | on lines 1 and 3 """ """ 36 | """ 37 | 38 | 39 | import string 40 | 41 | from yamllint.linter import LintProblem 42 | 43 | ID = 'trailing-spaces' 44 | TYPE = 'line' 45 | 46 | 47 | def check(conf, line): 48 | if line.end == 0: 49 | return 50 | 51 | # YAML recognizes two white space characters: space and tab. 52 | # http://yaml.org/spec/1.2/spec.html#id2775170 53 | 54 | pos = line.end 55 | while line.buffer[pos - 1] in string.whitespace and pos > line.start: 56 | pos -= 1 57 | 58 | if pos != line.end and line.buffer[pos] in ' \t': 59 | yield LintProblem(line.line_no, pos - line.start + 1, 60 | 'trailing spaces') 61 | -------------------------------------------------------------------------------- /yamllint/rules/truthy.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Peter Ericson 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | """ 17 | Use this rule to forbid non-explicitly typed truthy values other than allowed 18 | ones (by default: ``true`` and ``false``), for example ``YES`` or ``off``. 19 | 20 | This can be useful to prevent surprises from YAML parsers transforming 21 | ``[yes, FALSE, Off]`` into ``[true, false, false]`` or 22 | ``{y: 1, yes: 2, on: 3, true: 4, True: 5}`` into ``{y: 1, true: 5}``. 23 | 24 | Depending on the YAML specification version used by the YAML document, the list 25 | of truthy values can differ. In YAML 1.2, only capitalized / uppercased 26 | combinations of ``true`` and ``false`` are considered truthy, whereas in YAML 27 | 1.1 combinations of ``yes``, ``no``, ``on`` and ``off`` are too. To make the 28 | YAML specification version explicit in a YAML document, a ``%YAML 1.2`` 29 | directive can be used (see example below). 30 | 31 | .. rubric:: Options 32 | 33 | * ``allowed-values`` defines the list of truthy values which will be ignored 34 | during linting. The default is ``['true', 'false']``, but can be changed to 35 | any list containing: ``'TRUE'``, ``'True'``, ``'true'``, ``'FALSE'``, 36 | ``'False'``, ``'false'``, ``'YES'``, ``'Yes'``, ``'yes'``, ``'NO'``, 37 | ``'No'``, ``'no'``, ``'ON'``, ``'On'``, ``'on'``, ``'OFF'``, ``'Off'``, 38 | ``'off'``. 39 | * ``check-keys`` disables verification for keys in mappings. By default, 40 | ``truthy`` rule applies to both keys and values. Set this option to ``false`` 41 | to prevent this. 42 | 43 | .. rubric:: Default values (when enabled) 44 | 45 | .. code-block:: yaml 46 | 47 | rules: 48 | truthy: 49 | allowed-values: ['true', 'false'] 50 | check-keys: true 51 | 52 | .. rubric:: Examples 53 | 54 | #. With ``truthy: {}`` 55 | 56 | the following code snippet would **PASS**: 57 | :: 58 | 59 | boolean: true 60 | 61 | object: {"True": 1, 1: "True"} 62 | 63 | "yes": 1 64 | "on": 2 65 | "True": 3 66 | 67 | explicit: 68 | string1: !!str True 69 | string2: !!str yes 70 | string3: !!str off 71 | encoded: !!binary | 72 | True 73 | OFF 74 | pad== # this decodes as 'N\xbb\x9e8Qii' 75 | boolean1: !!bool true 76 | boolean2: !!bool "false" 77 | boolean3: !!bool FALSE 78 | boolean4: !!bool True 79 | boolean5: !!bool off 80 | boolean6: !!bool NO 81 | 82 | the following code snippet would **FAIL**: 83 | :: 84 | 85 | object: {True: 1, 1: True} 86 | 87 | the following code snippet would **FAIL**: 88 | :: 89 | 90 | %YAML 1.1 91 | --- 92 | yes: 1 93 | on: 2 94 | True: 3 95 | 96 | the following code snippet would **PASS**: 97 | :: 98 | 99 | %YAML 1.2 100 | --- 101 | yes: 1 102 | on: 2 103 | true: 3 104 | 105 | #. With ``truthy: {allowed-values: ["yes", "no"]}`` 106 | 107 | the following code snippet would **PASS**: 108 | :: 109 | 110 | - yes 111 | - no 112 | - "true" 113 | - 'false' 114 | - foo 115 | - bar 116 | 117 | the following code snippet would **FAIL**: 118 | :: 119 | 120 | - true 121 | - false 122 | - on 123 | - off 124 | 125 | #. With ``truthy: {check-keys: false}`` 126 | 127 | the following code snippet would **PASS**: 128 | :: 129 | 130 | yes: 1 131 | on: 2 132 | true: 3 133 | 134 | the following code snippet would **FAIL**: 135 | :: 136 | 137 | yes: Yes 138 | on: On 139 | true: True 140 | """ 141 | 142 | import yaml 143 | 144 | from yamllint.linter import LintProblem 145 | 146 | TRUTHY_1_1 = ['YES', 'Yes', 'yes', 147 | 'NO', 'No', 'no', 148 | 'TRUE', 'True', 'true', 149 | 'FALSE', 'False', 'false', 150 | 'ON', 'On', 'on', 151 | 'OFF', 'Off', 'off'] 152 | TRUTHY_1_2 = ['TRUE', 'True', 'true', 153 | 'FALSE', 'False', 'false'] 154 | 155 | 156 | ID = 'truthy' 157 | TYPE = 'token' 158 | CONF = {'allowed-values': TRUTHY_1_1.copy(), 'check-keys': bool} 159 | DEFAULT = {'allowed-values': ['true', 'false'], 'check-keys': True} 160 | 161 | 162 | def yaml_spec_version_for_document(context): 163 | if 'yaml_spec_version' in context: 164 | return context['yaml_spec_version'] 165 | return (1, 1) 166 | 167 | 168 | def check(conf, token, prev, next, nextnext, context): 169 | if isinstance(token, yaml.tokens.DirectiveToken) and token.name == 'YAML': 170 | context['yaml_spec_version'] = token.value 171 | elif isinstance(token, yaml.tokens.DocumentEndToken): 172 | context.pop('yaml_spec_version', None) 173 | context.pop('bad_truthy_values', None) 174 | 175 | if prev and isinstance(prev, yaml.tokens.TagToken): 176 | return 177 | 178 | if (not conf['check-keys'] and isinstance(prev, yaml.tokens.KeyToken) and 179 | isinstance(token, yaml.tokens.ScalarToken)): 180 | return 181 | 182 | if isinstance(token, yaml.tokens.ScalarToken) and token.style is None: 183 | if 'bad_truthy_values' not in context: 184 | context['bad_truthy_values'] = set( 185 | TRUTHY_1_2 if yaml_spec_version_for_document(context) == (1, 2) 186 | else TRUTHY_1_1) 187 | context['bad_truthy_values'] -= set(conf['allowed-values']) 188 | 189 | if token.value in context['bad_truthy_values']: 190 | yield LintProblem(token.start_mark.line + 1, 191 | token.start_mark.column + 1, 192 | "truthy value should be one of [" + 193 | ", ".join(sorted(conf['allowed-values'])) + "]") 194 | --------------------------------------------------------------------------------