Repository: ansible/ansible

Issue:
Ansible 2.5.0: jinja2 abs-filter triggers AttributeError and deprecation warning
<!---
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
<!--- Pick one below and delete the rest -->
 - 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
-->
Templating (jinja2)

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
ansible 2.5.0
  config file = <REPO_PATH>/ansible.cfg
  configured module search path = [u'<USER_PATH>/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python2.7/site-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 2.7.13 (default, Jul 18 2017, 09:17:00) [GCC 4.2.1 Compatible Apple LLVM 8.1.0 (clang-802.0.42)]
```

##### 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).
-->
```
DEFAULT_FORCE_HANDLERS(<REPO_PATH>/ansible.cfg) = True
DEFAULT_VAULT_PASSWORD_FILE(<REPO_PATH>/ansible.cfg) = <REPO_PATH>/secure/ansible.vault.pwd
RETRY_FILES_ENABLED(<REPO_PATH>/ansible.cfg) = True
RETRY_FILES_SAVE_PATH(<REPO_PATH>/ansible.cfg) = <USER_PATH>/.ansible/retry-files
```

##### 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.
-->
Mac OS 10.13.3

##### SUMMARY
Starting with Ansible v2.5.0 jinja2 `abs`-math filter triggers `AttributeError: 'int' object has no attribute 'startswith'` and also prints _"[DEPRECATION WARNING]: Using tests as filters is deprecated. Instead of using `result|abs` instead use `result is abs`."_ which I think is invalid in this case.

##### STEPS TO REPRODUCE

Run the following task:
```
    - debug:
        msg: '{{(5 - 10) | abs}}'
```

Output:
```
...
TASK [debug] ****************************************************************************************************************************************************************************
[DEPRECATION WARNING]: Using tests as filters is deprecated. Instead of using `result|abs` instead use `result is abs`. This feature will be removed in version 2.9. Deprecation
warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: AttributeError: 'int' object has no attribute 'startswith'
fatal: [main-test]: FAILED! => {"msg": "Unexpected failure during module execution.", "stdout": ""}
	to retry, use: --limit @/Users/aske/.ansible/retry-files/site.retry
...
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
The expression should be evaluated correctly as with Ansible v2.4.

##### ACTUAL RESULTS
See error and deprecation warning above.


Discussion:
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 --->
I have a fix for this, and will get it pushed up soon.
@sivel i got 
```
AnsibleError: An unhandled exception occurred while templating '{{ (memcached_max_clients * 1.1)  | int | abs}}'. Error was a <type 'exceptions.AttributeError'>, original message: 'int' object has no attribute 'startswith'
```
do i need to open separate issue ? 
@freeseacher No, your issue is the same.

Resolving patch:
diff --git a/lib/ansible/playbook/conditional.py b/lib/ansible/playbook/conditional.py
--- a/lib/ansible/playbook/conditional.py
+++ b/lib/ansible/playbook/conditional.py
@@ -176,7 +176,7 @@ def generic_visit(self, node, inside_call=False, inside_yield=False):
                         )
             try:
                 e = templar.environment.overlay()
-                e.filters.update(templar._get_filters())
+                e.filters.update(templar._get_filters(e.filters))
                 e.tests.update(templar._get_tests())
 
                 res = e._parse(conditional, None, None)
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
@@ -292,7 +292,7 @@ def __init__(self, loader, shared_loader_obj=None, variables=None):
         ))
         self._no_type_regex = re.compile(r'.*\|\s*(?:%s)\s*(?:%s)?$' % ('|'.join(C.STRING_TYPE_FILTERS), self.environment.variable_end_string))
 
-    def _get_filters(self):
+    def _get_filters(self, builtin_filters):
         '''
         Returns filter plugins, after loading and caching them if need be
         '''
@@ -308,6 +308,9 @@ def _get_filters(self):
 
         # TODO: Remove registering tests as filters in 2.9
         for name, func in self._get_tests().items():
+            if name in builtin_filters:
+                # If we have a custom test named the same as a builtin filter, don't register as a filter
+                continue
             self._filters[name] = tests_as_filters_warning(name, func)
 
         return self._filters.copy()
@@ -681,7 +684,7 @@ def do_template(self, data, preserve_trailing_newlines=True, escape_backslashes=
                     setattr(myenv, key, ast.literal_eval(val.strip()))
 
             # Adds Ansible custom filters and tests
-            myenv.filters.update(self._get_filters())
+            myenv.filters.update(self._get_filters(myenv.filters))
             myenv.tests.update(self._get_tests())
 
             if escape_backslashes:


Test patch:
diff --git a/test/units/template/test_tests_as_filters_warning.py b/test/units/template/test_tests_as_filters_warning.py
--- a/test/units/template/test_tests_as_filters_warning.py
+++ b/test/units/template/test_tests_as_filters_warning.py
@@ -1,5 +1,7 @@
 from ansible.template import Templar, display
 from units.mock.loader import DictDataLoader
+from jinja2.filters import FILTERS
+from os.path import isabs
 
 
 def test_tests_as_filters_warning(mocker):
@@ -7,7 +9,7 @@ def test_tests_as_filters_warning(mocker):
         "/path/to/my_file.txt": "foo\n",
     })
     templar = Templar(loader=fake_loader, variables={})
-    filters = templar._get_filters()
+    filters = templar._get_filters(templar.environment.filters)
 
     mocker.patch.object(display, 'deprecated')
 
@@ -28,3 +30,6 @@ def test_tests_as_filters_warning(mocker):
     display.deprecated.reset_mock()
     filters['bool'](True)
     assert display.deprecated.call_count == 0
+
+    # Ensure custom test does not override builtin filter
+    assert filters.get('abs') != isabs