Repository: ansible/ansible

Issue:
STRING_TYPE_FILTERS not recognised when used with arguments
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and devel branch are affected too.
Always add information AFTER of these html comments.
-->

##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
<!---
Insert below this comment the name of the module, plugin, task or feature.
Do not include extra details here, e.g. "vyos_command" not "the network module vyos_command" or the full path
-->
template

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
ansible 2.4.3.0
  config file = None
  configured module search path = [u'/home/bruce/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /home/bruce/work/sdp/env/local/lib/python2.7/site-packages/ansible
  executable location = /home/bruce/work/sdp/env/bin/ansible
  python version = 2.7.12 (default, Nov 19 2016, 06:48:10) [GCC 5.4.0 20160609]
```

##### CONFIGURATION
<!---
If using Ansible 2.4 or above, paste the results of "ansible-config dump --only-changed"
Otherwise, mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).
-->
No ansible.cfg

##### OS / ENVIRONMENT
<!---
Mention the OS you are running Ansible from, and the OS you are
managing, or say "N/A" for anything that is not platform-specific.
Also mention the specific version of what you are trying to control,
e.g. if this is a network bug the version of firmware on the network device.
-->
Ubuntu 16.04

##### SUMMARY
<!--- Explain the problem briefly -->
When passing the `indent=` option to the `to_nice_json` filter (as described [here](https://docs.ansible.com/ansible/latest/playbooks_filters.html#filters-for-formatting-data)), the output JSON is all on one line, as if from plain `to_json`.

##### STEPS TO REPRODUCE
<!---
For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used.
-->

<!--- Paste example playbooks or commands between quotes below -->
```yaml
---
- hosts: localhost
  vars:
    doc:
      hello: world
      xyz: abc
  tasks:
    - name: test
      copy:
        content: "{{doc | to_nice_json(indent=2)}}\n"
        dest: "./test.out"
```

Run simply with `ansible-playbook test.yml` (no inventory needed).

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
Expected `test.out` to contain a multi-line pretty-printing of the content e.g.
```
{
  "hello": "world", 
  "xyz": "abc"
}
```
When the `indent` option is not specified, this happens (with a 4 character indent).

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->

<!--- Paste verbatim command output between quotes below -->
test.out contains
```
{"xyz": "abc", "hello": "world"}
```



Discussion:
Files identified in the description:
None

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
I poked around a bit, and it looks like the problem is that when Ansible expands a template and sees something "looks like" a list or dict, it turns it into a list/dict. There is a `_no_type_regex` that is constructed to identify special cases where this shouldn't be done (based on a list of filters which are known to return strings that shouldn't be parsed), but the regex doesn't allow for arguments to the filter. 

It's probably impossible to exactly match a list of arguments with a regex, because a regex can't recognise a balanced set of parentheses (actually, a regex is dodgy anyway because it doesn't take string literals into account).

A workaround is to add `| string` to the expression: `string` is another filter that's listed in STRING_TYPE_FILTERS, and since it doesn't have arguments it's picked up.
Files identified in the description:
* [lib/ansible/modules/files/template.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/files/template.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->

Resolving patch:
diff --git a/lib/ansible/template/__init__.py b/lib/ansible/template/__init__.py
--- a/lib/ansible/template/__init__.py
+++ b/lib/ansible/template/__init__.py
@@ -290,7 +290,8 @@ def __init__(self, loader, shared_loader_obj=None, variables=None):
             self.environment.block_end_string,
             self.environment.variable_end_string
         ))
-        self._no_type_regex = re.compile(r'.*\|\s*(?:%s)\s*(?:%s)?$' % ('|'.join(C.STRING_TYPE_FILTERS), self.environment.variable_end_string))
+        self._no_type_regex = re.compile(r'.*?\|\s*(?:%s)(?:\([^\|]*\))?\s*\)?\s*(?:%s)' %
+                                         ('|'.join(C.STRING_TYPE_FILTERS), self.environment.variable_end_string))
 
     def _get_filters(self, builtin_filters):
         '''


Test patch: