Fix docs syntax highlighting errors (#50836)

* Add support for [WARNING]: ...

* Fix unreachable/failed output lexing.

* Detecting retry/--limit lines.

* Removing strange (invisible) characters which cause lexing problems.

* Using better-fitting lexers.

* Improve YAML lexing: don't accept quotes in keys.

* Add Django lexer (unchanged) from Pygments.

* Add support for != and % operators.
pull/4420/head
Felix Fontein 2019-01-24 23:09:41 +01:00 committed by Alicia Cozine
parent 6345ea2925
commit f6122fb63b
9 changed files with 164 additions and 35 deletions

View File

@ -37,9 +37,11 @@
from __future__ import absolute_import, print_function from __future__ import absolute_import, print_function
from pygments.lexer import LexerContext, ExtendedRegexLexer, DelegatingLexer, RegexLexer, bygroups, include from pygments.lexer import LexerContext, ExtendedRegexLexer, DelegatingLexer, RegexLexer, bygroups, include
from pygments.lexers import DjangoLexer, DiffLexer from pygments.lexers import DiffLexer
from pygments import token from pygments import token
import re
class AnsibleYamlLexerContext(LexerContext): class AnsibleYamlLexerContext(LexerContext):
"""Indentation context for the YAML lexer.""" """Indentation context for the YAML lexer."""
@ -255,7 +257,7 @@ class AnsibleYamlLexer(ExtendedRegexLexer):
# whitespaces separating tokens # whitespaces separating tokens
(r'[ ]+', token.Text), (r'[ ]+', token.Text),
# key with colon # key with colon
(r'([^,:?\[\]{}\n]+)(:)(?=[ ]|$)', (r'''([^,:?\[\]{}"'\n]+)(:)(?=[ ]|$)''',
bygroups(token.Name.Tag, set_indent(token.Punctuation, implicit=True))), bygroups(token.Name.Tag, set_indent(token.Punctuation, implicit=True))),
# tags, anchors and aliases, # tags, anchors and aliases,
include('descriptors'), include('descriptors'),
@ -334,7 +336,7 @@ class AnsibleYamlLexer(ExtendedRegexLexer):
# a flow mapping indicated by '{' and '}' # a flow mapping indicated by '{' and '}'
'flow-mapping': [ 'flow-mapping': [
# key with colon # key with colon
(r'([^,:?\[\]{}\n]+)(:)(?=[ ]|$)', (r'''([^,:?\[\]{}"'\n]+)(:)(?=[ ]|$)''',
bygroups(token.Name.Tag, token.Punctuation)), bygroups(token.Name.Tag, token.Punctuation)),
# include flow collection rules # include flow collection rules
include('flow-collection'), include('flow-collection'),
@ -458,6 +460,89 @@ class AnsibleYamlLexer(ExtendedRegexLexer):
return super(AnsibleYamlLexer, self).get_tokens_unprocessed(text, context) return super(AnsibleYamlLexer, self).get_tokens_unprocessed(text, context)
class AnsibleDjangoLexer(RegexLexer):
"""
Generic `django <http://www.djangoproject.com/documentation/templates/>`_
and `jinja <http://wsgiarea.pocoo.org/jinja/>`_ template lexer.
It just highlights django/jinja code between the preprocessor directives,
other data is left untouched by the lexer.
"""
name = 'Django/Jinja'
aliases = ['django', 'jinja']
mimetypes = ['application/x-django-templating', 'application/x-jinja']
flags = re.M | re.S
tokens = {
'root': [
(r'[^{]+', token.Other),
(r'\{\{', token.Comment.Preproc, 'var'),
# jinja/django comments
(r'\{[*#].*?[*#]\}', token.Comment),
# django comments
(r'(\{%)(-?\s*)(comment)(\s*-?)(%\})(.*?)'
r'(\{%)(-?\s*)(endcomment)(\s*-?)(%\})',
bygroups(token.Comment.Preproc, token.Text, token.Keyword, token.Text, token.Comment.Preproc,
token.Comment, token.Comment.Preproc, token.Text, token.Keyword, token.Text,
token.Comment.Preproc)),
# raw jinja blocks
(r'(\{%)(-?\s*)(raw)(\s*-?)(%\})(.*?)'
r'(\{%)(-?\s*)(endraw)(\s*-?)(%\})',
bygroups(token.Comment.Preproc, token.Text, token.Keyword, token.Text, token.Comment.Preproc,
token.Text, token.Comment.Preproc, token.Text, token.Keyword, token.Text,
token.Comment.Preproc)),
# filter blocks
(r'(\{%)(-?\s*)(filter)(\s+)([a-zA-Z_]\w*)',
bygroups(token.Comment.Preproc, token.Text, token.Keyword, token.Text, token.Name.Function),
'block'),
(r'(\{%)(-?\s*)([a-zA-Z_]\w*)',
bygroups(token.Comment.Preproc, token.Text, token.Keyword), 'block'),
(r'\{', token.Other)
],
'varnames': [
(r'(\|)(\s*)([a-zA-Z_]\w*)',
bygroups(token.Operator, token.Text, token.Name.Function)),
(r'(is)(\s+)(not)?(\s+)?([a-zA-Z_]\w*)',
bygroups(token.Keyword, token.Text, token.Keyword, token.Text, token.Name.Function)),
(r'(_|true|false|none|True|False|None)\b', token.Keyword.Pseudo),
(r'(in|as|reversed|recursive|not|and|or|is|if|else|import|'
r'with(?:(?:out)?\s*context)?|scoped|ignore\s+missing)\b',
token.Keyword),
(r'(loop|block|super|forloop)\b', token.Name.Builtin),
(r'[a-zA-Z_][\w-]*', token.Name.Variable),
(r'\.\w+', token.Name.Variable),
(r':?"(\\\\|\\"|[^"])*"', token.String.Double),
(r":?'(\\\\|\\'|[^'])*'", token.String.Single),
(r'([{}()\[\]+\-*/%,:~]|[><=]=?|!=)', token.Operator),
(r"[0-9](\.[0-9]*)?(eE[+-][0-9])?[flFLdD]?|"
r"0[xX][0-9a-fA-F]+[Ll]?", token.Number),
],
'var': [
(r'\s+', token.Text),
(r'(-?)(\}\})', bygroups(token.Text, token.Comment.Preproc), '#pop'),
include('varnames')
],
'block': [
(r'\s+', token.Text),
(r'(-?)(%\})', bygroups(token.Text, token.Comment.Preproc), '#pop'),
include('varnames'),
(r'.', token.Punctuation)
]
}
def analyse_text(text):
rv = 0.0
if re.search(r'\{%\s*(block|extends)', text) is not None:
rv += 0.4
if re.search(r'\{%\s*if\s*.*?%\}', text) is not None:
rv += 0.1
if re.search(r'\{\{.*?\}\}', text) is not None:
rv += 0.1
return rv
class AnsibleYamlJinjaLexer(DelegatingLexer): class AnsibleYamlJinjaLexer(DelegatingLexer):
""" """
Subclass of the `DjangoLexer` that highlights unlexed data with the Subclass of the `DjangoLexer` that highlights unlexed data with the
@ -474,7 +559,7 @@ class AnsibleYamlJinjaLexer(DelegatingLexer):
mimetypes = ['text/x-yaml+jinja'] mimetypes = ['text/x-yaml+jinja']
def __init__(self, **options): def __init__(self, **options):
super(AnsibleYamlJinjaLexer, self).__init__(AnsibleYamlLexer, DjangoLexer, **options) super(AnsibleYamlJinjaLexer, self).__init__(AnsibleYamlLexer, AnsibleDjangoLexer, **options)
class AnsibleOutputPrimaryLexer(RegexLexer): class AnsibleOutputPrimaryLexer(RegexLexer):
@ -555,8 +640,8 @@ class AnsibleOutputPrimaryLexer(RegexLexer):
], ],
'host-error': [ 'host-error': [
(r'(?:( )(UNREACHABLE|FAILED)(!))?', (r'(?:(:)( )(UNREACHABLE|FAILED)(!))?',
bygroups(token.Text, token.Keyword, token.Punctuation), bygroups(token.Punctuation, token.Text, token.Keyword, token.Punctuation),
'host-postfix'), 'host-postfix'),
(r'', token.Text, 'host-postfix'), (r'', token.Text, 'host-postfix'),
], ],
@ -579,9 +664,12 @@ class AnsibleOutputPrimaryLexer(RegexLexer):
(r'(fatal|ok|changed|skipping)(:)( )', (r'(fatal|ok|changed|skipping)(:)( )',
bygroups(token.Keyword, token.Punctuation, token.Text), bygroups(token.Keyword, token.Punctuation, token.Text),
'host-name'), 'host-name'),
(r'(\[)(WARNING)(\]:)([^\n]+)',
bygroups(token.Punctuation, token.Keyword, token.Punctuation, token.Text)),
(r'([^ ]+)( +)(:)', (r'([^ ]+)( +)(:)',
bygroups(token.Name, token.Text, token.Punctuation), bygroups(token.Name, token.Text, token.Punctuation),
'host-result'), 'host-result'),
(r'(\tto retry, use: )(.*)(\n)', bygroups(token.Text, token.Literal.String, token.Text)),
(r'.*\n', token.Other), (r'.*\n', token.Other),
], ],
} }
@ -609,7 +697,12 @@ def setup(app):
""" Initializer for Sphinx extension API. """ Initializer for Sphinx extension API.
See http://www.sphinx-doc.org/en/stable/extdev/index.html#dev-extensions. See http://www.sphinx-doc.org/en/stable/extdev/index.html#dev-extensions.
""" """
for lexer in [AnsibleYamlLexer(startinline=True), AnsibleYamlJinjaLexer(startinline=True), AnsibleOutputLexer(startinline=True)]: for lexer in [
AnsibleDjangoLexer(startinline=True),
AnsibleYamlLexer(startinline=True),
AnsibleYamlJinjaLexer(startinline=True),
AnsibleOutputLexer(startinline=True)
]:
app.add_lexer(lexer.name, lexer) app.add_lexer(lexer.name, lexer)
for alias in lexer.aliases: for alias in lexer.aliases:
app.add_lexer(alias, lexer) app.add_lexer(alias, lexer)

View File

@ -56,7 +56,7 @@ To create a new module:
3. Paste the content below into your new module file. It includes the :ref:`required Ansible format and documentation <developing_modules_documenting>` and some example code. 3. Paste the content below into your new module file. It includes the :ref:`required Ansible format and documentation <developing_modules_documenting>` and some example code.
4. Modify and extend the code to do what you want your new module to do. See the :ref:`programming tips <developing_modules_best_practices>` and :ref:`Python 3 compatibility <developing_python_3>` pages for pointers on writing clean, concise module code. 4. Modify and extend the code to do what you want your new module to do. See the :ref:`programming tips <developing_modules_best_practices>` and :ref:`Python 3 compatibility <developing_python_3>` pages for pointers on writing clean, concise module code.
.. code:: python .. code-block:: python
#!/usr/bin/python #!/usr/bin/python

View File

@ -63,34 +63,46 @@ Sphinx will 'learn on the fly' when creating a hierarchy of headers.
To make our documents easy to read and to edit, we follow a standard set of header notations. To make our documents easy to read and to edit, we follow a standard set of header notations.
We use: We use:
* ``###`` with overline, for parts:: * ``###`` with overline, for parts:
.. code-block:: rst
############### ###############
Developer guide Developer guide
############### ###############
* ``***`` with overline, for chapters:: * ``***`` with overline, for chapters:
.. code-block:: rst
******************* *******************
Ansible style guide Ansible style guide
******************* *******************
* ``===`` for sections:: * ``===`` for sections:
.. code-block:: rst
Mechanical guidelines Mechanical guidelines
===================== =====================
* ``---`` for subsections:: * ``---`` for subsections:
.. code-block:: rst
Internal navigation Internal navigation
------------------- -------------------
* ``^^^`` for sub-subsections:: * ``^^^`` for sub-subsections:
.. code-block:: rst
Adding anchors Adding anchors
^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^
* ``"""`` for paragraphs:: * ``"""`` for paragraphs:
.. code-block:: rst
Paragraph that needs a title Paragraph that needs a title
"""""""""""""""""""""""""""" """"""""""""""""""""""""""""
@ -120,7 +132,9 @@ Adding anchors
Adding internal links Adding internal links
^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
* All internal links must use ``:ref:`` syntax. These links both point to the anchor defined above:: * All internal links must use ``:ref:`` syntax. These links both point to the anchor defined above:
.. code-block:: rst
:ref:`unique_page` :ref:`unique_page`
:ref:`this page <unique_page>` :ref:`this page <unique_page>`
@ -137,7 +151,9 @@ If you include a local TOC:
* use the ``:local:`` directive so the page's main header is not included * use the ``:local:`` directive so the page's main header is not included
* do not include a title * do not include a title
The syntax is:: The syntax is:
.. code-block:: rst
.. contents:: .. contents::
:local: :local:

View File

@ -55,7 +55,9 @@ Or for the openstack plugin:
# clouds.yml # clouds.yml
plugin: openstack plugin: openstack
The ``auto`` inventory plugin is enabled by default and works by using the ``plugin`` field to indicate the plugin that should attempt to parse it. You can configure the whitelist/precedence of inventory plugins used to parse source using the `ansible.cfg` ['inventory'] ``enable_plugins`` list. After enabling the plugin and providing any required options you can view the populated inventory with ``ansible-inventory -i demo.aws_ec2.yml --graph``:: The ``auto`` inventory plugin is enabled by default and works by using the ``plugin`` field to indicate the plugin that should attempt to parse it. You can configure the whitelist/precedence of inventory plugins used to parse source using the `ansible.cfg` ['inventory'] ``enable_plugins`` list. After enabling the plugin and providing any required options you can view the populated inventory with ``ansible-inventory -i demo.aws_ec2.yml --graph``:
.. code-block:: text
@all: @all:
|--@aws_ec2: |--@aws_ec2:
@ -88,7 +90,9 @@ You can create dynamic groups using host variables with the constructed ``keyed_
# set the ansible_host variable to connect with the private IP address without changing the hostname # set the ansible_host variable to connect with the private IP address without changing the hostname
ansible_host: private_ip_address ansible_host: private_ip_address
Now the output of ``ansible-inventory -i demo.aws_ec2.yml --graph``:: Now the output of ``ansible-inventory -i demo.aws_ec2.yml --graph``:
.. code-block:: text
@all: @all:
|--@aws_ec2: |--@aws_ec2:

View File

@ -41,24 +41,20 @@ Using lookup plugins
Lookup plugins can be used anywhere you can use templating in Ansible: in a play, in variables file, or in a Jinja2 template for the :ref:`template <template_module>` module. Lookup plugins can be used anywhere you can use templating in Ansible: in a play, in variables file, or in a Jinja2 template for the :ref:`template <template_module>` module.
.. code-block:: yaml .. code-block:: YAML+Jinja
vars: vars:
file_contents: "{{lookup('file', 'path/to/file.txt')}}" file_contents: "{{lookup('file', 'path/to/file.txt')}}"
Lookups are an integral part of loops. Wherever you see ``with_``, the part after the underscore is the name of a lookup. Lookups are an integral part of loops. Wherever you see ``with_``, the part after the underscore is the name of a lookup.
This is also the reason most lookups output lists and take lists as input; for example, ``with_items`` uses the :ref:`items <items_lookup>` lookup: This is also the reason most lookups output lists and take lists as input; for example, ``with_items`` uses the :ref:`items <items_lookup>` lookup::
.. code-block:: yaml
tasks: tasks:
- name: count to 3 - name: count to 3
debug: msg={{item}} debug: msg={{item}}
with_items: [1, 2, 3] with_items: [1, 2, 3]
You can combine lookups with :ref:`playbooks_filters`, :ref:`playbooks_tests` and even each other to do some complex data generation and manipulation. For example: You can combine lookups with :ref:`playbooks_filters`, :ref:`playbooks_tests` and even each other to do some complex data generation and manipulation. For example::
.. code-block:: yaml
tasks: tasks:
- name: valid but useless and over complicated chained lookups and filters - name: valid but useless and over complicated chained lookups and filters
@ -77,6 +73,8 @@ To ignore errors::
- name: file doesnt exist, but i dont care .. file plugin itself warns anyways ... - name: file doesnt exist, but i dont care .. file plugin itself warns anyways ...
debug: msg="{{ lookup('file', '/idontexist', errors='ignore') }}" debug: msg="{{ lookup('file', '/idontexist', errors='ignore') }}"
.. code-block:: ansible-output
[WARNING]: Unable to find '/idontexist' in expected paths (use -vvvvv to see paths) [WARNING]: Unable to find '/idontexist' in expected paths (use -vvvvv to see paths)
ok: [localhost] => { ok: [localhost] => {
@ -89,6 +87,8 @@ To get a warning instead of a failure::
- name: file doesnt exist, let me know, but continue - name: file doesnt exist, let me know, but continue
debug: msg="{{ lookup('file', '/idontexist', errors='warn') }}" debug: msg="{{ lookup('file', '/idontexist', errors='warn') }}"
.. code-block:: ansible-output
[WARNING]: Unable to find '/idontexist' in expected paths (use -vvvvv to see paths) [WARNING]: Unable to find '/idontexist' in expected paths (use -vvvvv to see paths)
[WARNING]: An unhandled exception occurred while running the lookup plugin 'file'. Error was a <class 'ansible.errors.AnsibleError'>, original message: could not locate file in lookup: /idontexist [WARNING]: An unhandled exception occurred while running the lookup plugin 'file'. Error was a <class 'ansible.errors.AnsibleError'>, original message: could not locate file in lookup: /idontexist
@ -103,6 +103,8 @@ Fatal error (the default)::
- name: file doesnt exist, FAIL (this is the default) - name: file doesnt exist, FAIL (this is the default)
debug: msg="{{ lookup('file', '/idontexist', errors='strict') }}" debug: msg="{{ lookup('file', '/idontexist', errors='strict') }}"
.. code-block:: ansible-output
[WARNING]: Unable to find '/idontexist' in expected paths (use -vvvvv to see paths) [WARNING]: Unable to find '/idontexist' in expected paths (use -vvvvv to see paths)
fatal: [localhost]: FAILED! => {"msg": "An unhandled exception occurred while running the lookup plugin 'file'. Error was a <class 'ansible.errors.AnsibleError'>, original message: could not locate file in lookup: /idontexist"} fatal: [localhost]: FAILED! => {"msg": "An unhandled exception occurred while running the lookup plugin 'file'. Error was a <class 'ansible.errors.AnsibleError'>, original message: could not locate file in lookup: /idontexist"}
@ -120,7 +122,9 @@ The default behavior of ``lookup`` is to return a string of comma separated valu
This was done primarily to provide an easier and more consistent interface for interacting with the new ``loop`` keyword, while maintaining backwards compatibility with other uses of ``lookup``. This was done primarily to provide an easier and more consistent interface for interacting with the new ``loop`` keyword, while maintaining backwards compatibility with other uses of ``lookup``.
The following examples are equivalent:: The following examples are equivalent:
.. code-block:: jinja
lookup('dict', dict_variable, wantlist=True) lookup('dict', dict_variable, wantlist=True)
@ -128,7 +132,9 @@ The following examples are equivalent::
As demonstrated above the behavior of ``wantlist=True`` is implicit when using ``query``. As demonstrated above the behavior of ``wantlist=True`` is implicit when using ``query``.
Additionally, ``q`` was introduced as a shortform of ``query``:: Additionally, ``q`` was introduced as a shortform of ``query``:
.. code-block:: jinja
q('dict', dict_variable) q('dict', dict_variable)

View File

@ -170,7 +170,9 @@ you can use escapes::
The list of allowed escapes can be found in the YAML Specification under "Escape Sequences" (YAML 1.1) or "Escape Characters" (YAML 1.2). The list of allowed escapes can be found in the YAML Specification under "Escape Sequences" (YAML 1.1) or "Escape Characters" (YAML 1.2).
The following is invalid YAML:: The following is invalid YAML:
.. code-block:: text
foo: "an escaped \' single quote" foo: "an escaped \' single quote"

View File

@ -193,10 +193,14 @@ Create a file named ``scaleway_inventory.yml`` with the following content:
This inventory means that we want all hosts that got the tag ``web_server`` on the zones ``ams1`` and ``par1``. This inventory means that we want all hosts that got the tag ``web_server`` on the zones ``ams1`` and ``par1``.
Once you have configured this file, you can get the information using the following command: Once you have configured this file, you can get the information using the following command:
:: .. code-block:: bash
$ ansible-inventory --list -i scaleway_inventory.yml $ ansible-inventory --list -i scaleway_inventory.yml
The output will be:
.. code-block:: yaml
{ {
"_meta": { "_meta": {
"hostvars": { "hostvars": {
@ -251,7 +255,7 @@ As the Scaleway API is S3 compatible, Ansible supports it natively through the m
You can find many examples in ``./test/legacy/roles/scaleway_s3`` You can find many examples in ``./test/legacy/roles/scaleway_s3``
.. code-block:: yaml .. code-block:: yaml+jinja
- hosts: myserver - hosts: myserver
vars: vars:

View File

@ -100,7 +100,7 @@ of tasks running concurrently, you can do it this way::
- 5 - 5
durations: "{{ item }}" durations: "{{ item }}"
include_tasks: execute_batch.yml include_tasks: execute_batch.yml
loop: "{{ sleep_durations | batch(2) | list }}" loop: "{{ sleep_durations | batch(2) | list }}"
##################### #####################
# execute_batch.yml # execute_batch.yml

View File

@ -904,7 +904,9 @@ style. For example the following::
{{ "Plain style (default)" | comment }} {{ "Plain style (default)" | comment }}
will produce this output:: will produce this output:
.. code-block:: text
# #
# Plain style (default) # Plain style (default)
@ -923,7 +925,9 @@ above, you can customize it with::
{{ "My Special Case" | comment(decoration="! ") }} {{ "My Special Case" | comment(decoration="! ") }}
producing:: producing:
.. code-block:: text
! !
! My Special Case ! My Special Case
@ -935,7 +939,7 @@ It is also possible to fully customize the comment style::
That will create the following output: That will create the following output:
.. code-block:: sh .. code-block:: text
####### #######
# #
@ -1040,7 +1044,7 @@ To search a string with a regex, use the "regex_search" filter::
{{ 'ansible' | regex_search('(foobar)') }} {{ 'ansible' | regex_search('(foobar)') }}
# case insensitive search in multiline mode # case insensitive search in multiline mode
{{ 'foo\nBAR' | regex_search("^bar", multiline=True, ignorecase=True) }} {{ 'foo\nBAR' | regex_search("^bar", multiline=True, ignorecase=True) }}
To search for all occurrences of regex matches, use the "regex_findall" filter:: To search for all occurrences of regex matches, use the "regex_findall" filter::