Ansible modules are not the usual Python-3 porting exercise. There are two factors that make it harder to port them than most code:
The short answer is Python-3.4 and Python-2.4 but please read on for more information.
For Python-3 we are currently using Python-3.4 as a minimum. However, no long term supported Linux distributions currently ship with Python-3. When that occurs, we will probably take that as our minimum Python-3 version rather than Python-3.4. Thus far, Python-3 has been adding small changes that make it more compatible with Python-2 in its newer versions (For instance, Python-3.5 added the ability to use percent-formatted byte strings.) so it should be more pleasant to use a newer version of Python-3 if it’s available. At some point this will change but we’ll just have to cross that bridge when we get to it.
For Python-2 the default is for modules to run on Python-2.4. This allows users with older distributions that are stuck on Python-2.4 to manage their machines. Modules are allowed to drop support for Python-2.4 when one of their dependent libraries require a higher version of python. This is not an invitation to add unnecessary dependent libraries in order to force your module to be usable only with a newer version of Python. Instead it is an acknowledgment that some libraries (for instance, boto3 and docker-py) will only function with newer Python.
Note
When will we drop support for Python-2.4?
The only long term supported distro that we know of with Python-2.4 is RHEL5 (and its rebuilds like CentOS5) which is supported until April of 2017. We will likely end our support for Python-2.4 in modules in an Ansible release around that time. We know of no long term supported distributions with Python-2.5 so the new minimum Python-2 version will likely be Python-2.6. This will let us take advantage of the forwards-compat features of Python-2.6 so porting and maintainance of Python-2/Python-3 code will be easier after that.
Sometimes a module’s dependent libraries only run on Python-2 or only run on Python-3. We do not yet have a strategy for these modules but we’ll need to come up with one. I see three possibilities:
#!/usr/bin/python means the module will run on both Python-2 and
Python-3, #!/usr/bin/python2 means the module will only run on
Python-2, and #!/usr/bin/python3 means the module will only run on
Python-3. Ansible’s code will need to be modified to accommodate this.
For python2, if ansible_python2_interpreter is not set, it
will have to fallback to `` ansible_python_interpreter`` and if that’s not
set, fallback to /usr/bin/python. For python3, Ansible
will have to first try ansible_python3_interpreter and then fallback to
/usr/bin/python3 as normal.Methods 2 and 3 will both require that we modify modules or otherwise add this additional information somewhere. 2 needs only a little code changes in executor/module_common.py to parse. 3 will require a lot of work. This is probably not worthwhile if this is the only change but could be worthwhile if there’s other things as well. 1 requires that we port all modules to work with python3 syntax but only the code path to get to the library import being attempted and then a fail_json() being called because the libraries are unavailable needs to actually work.
In code which already needs Python-2.6+ (For instance, because a library it depends on only runs on Python >= 2.6) it is okay to port directly to the new exception catching syntax:
try:
a = 2/0
except ValueError as e:
module.fail_json(msg="Tried to divide by zero!")
For modules which also run on Python-2.4, we have to use an uglier construction to make this work under both Python-2.4 and Python-3:
from ansible.module_utils.pycompat24 import get_exception
[...]
try:
a = 2/0
except ValueError:
e = get_exception()
module.fail_json(msg="Tried to divide by zero!")
In Python-2.4, octal literals are specified as 0755. In Python-3, that is
invalid and octals must be specified as 0o755. To bridge this gap,
modules should create their octals like this:
# Can't use 0755 on Python-3 and can't use 0o755 on Python-2.4
EXECUTABLE_PERMS = int('0755', 8)
The third-party python-six library exists to help projects create code that runs on both Python-2 and Python-3. Ansible includes version 1.4.1 in module_utils so that other modules can use it without requiring that it is installed on the remote system. To make use of it, import it like this:
from ansible.module_utils import six
Note
Why version 1.4.1?
six-1.4.1 is the last version of python-six to support Python-2.4. As long as Ansible modules need to run on Python-2.4 we won’t be able to update the bundled copy of six.
We have travis compiling all modules with various versions of Python to check that the modules conform to the syntax at those versions. When you’ve ported a module so that its syntax works with Python-3, we need to modify .travis.yml so that the module is included in the syntax check. Here’s the relevant section of .travis.yml:
env:
global:
- PY3_EXCLUDE_LIST="cloud/amazon/cloudformation.py
cloud/amazon/ec2_ami.py
[...]
utilities/logic/wait_for.py"
The PY3_EXCLUDE_LIST environment variable is a blacklist of modules
which should not be tested (because we know that they are older modules which
have not yet been ported to pass the Python-3 syntax checks. To get another
old module to compile with Python-3, remove the entry for it from the list.
The goal is to have the LIST be empty.