Repository: ansible/ansible

Issue:
py3.6 error with ansible galaxy
##### ISSUE TYPE

 - Bug Report

##### COMPONENT NAME

ansible-galaxy

##### ANSIBLE VERSION

```
ansible 2.4.1.0
```

##### CONFIGURATION

```
$ ansible-config dump --only-changed
DEFAULT_VAULT_PASSWORD_FILE(env: ANSIBLE_VAULT_PASSWORD_FILE) = /Users/jodewey/.config/ansible-systems/vault-proxmox.pass
```

##### OS / ENVIRONMENT

Mac OS

##### SUMMARY

Python 3.6 issue with ansible-galaxy.

##### STEPS TO REPRODUCE

```
$ cat requirements.yml
---
- name: timezone
  src: yatesr.timezone
```

```
$ python --version
Python 3.6.1
```

```
$ ansible-galaxy install --force --role-file=requirements.yml --roles-path=roles -vvv
ansible-galaxy 2.4.1.0
  config file = None
  configured module search path = ['/Users/jodewey/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /Users/jodewey/.pyenv/versions/3.6.1/lib/python3.6/site-packages/ansible
  executable location = /Users/jodewey/.pyenv/versions/3.6.1/bin/ansible-galaxy
  python version = 3.6.1 (default, May  4 2017, 15:46:43) [GCC 4.2.1 Compatible Apple LLVM 8.1.0 (clang-802.0.42)]
No config file found; using defaults
Opened /Users/jodewey/.ansible_galaxy
found role {'name': 'timezone', 'src': 'yatesr.timezone', 'version': '', 'scm': None} in yaml file
Processing role timezone
Opened /Users/jodewey/.ansible_galaxy
- downloading role 'timezone', owned by yatesr
https://galaxy.ansible.com/api/v1/roles/?owner__username=yatesr&name=timezone
https://galaxy.ansible.com/api/v1/roles/1429/versions/?page_size=50
ERROR! Unexpected Exception, this is probably a bug: '<' not supported between instances of 'int' and 'str'
the full traceback was:

Traceback (most recent call last):
  File "/Users/jodewey/.pyenv/versions/3.6.1/bin/ansible-galaxy", line 106, in <module>
    exit_code = cli.run()
  File "/Users/jodewey/.pyenv/versions/3.6.1/lib/python3.6/site-packages/ansible/cli/galaxy.py", line 150, in run
    self.execute()
  File "/Users/jodewey/.pyenv/versions/3.6.1/lib/python3.6/site-packages/ansible/cli/__init__.py", line 154, in execute
    fn()
  File "/Users/jodewey/.pyenv/versions/3.6.1/lib/python3.6/site-packages/ansible/cli/galaxy.py", line 392, in execute_install
    installed = role.install()
  File "/Users/jodewey/.pyenv/versions/3.6.1/lib/python3.6/site-packages/ansible/galaxy/role.py", line 236, in install
    loose_versions.sort()
  File "/Users/jodewey/.pyenv/versions/3.6.1/lib/python3.6/distutils/version.py", line 52, in __lt__
    c = self._cmp(other)
  File "/Users/jodewey/.pyenv/versions/3.6.1/lib/python3.6/distutils/version.py", line 337, in _cmp
    if self.version < other.version:
TypeError: '<' not supported between instances of 'int' and 'str'

```

##### EXPECTED RESULTS

Role downloads

##### ACTUAL RESULTS

Exception raised


Discussion:
It is problem with Python 3.x.
In Python 3, distutils.LooseVersion fails to compare number and a word.
Bug already reported here. https://bugs.python.org/issue14894
If Ansible were to claim py3 support wouldn't galaxy work-a-round the issue then?  Waiting on an upstream py3 fix seems to be some time away.
Any update on this?  Not being able to use galaxy on py36 seems like an important bug.
I've created a pull request which should address this issue in a safe way with galaxy. Due to galaxy having no guarantees on version formatting, I think the solution in that PR is the best way to go.

Resolving patch:
diff --git a/lib/ansible/galaxy/role.py b/lib/ansible/galaxy/role.py
--- a/lib/ansible/galaxy/role.py
+++ b/lib/ansible/galaxy/role.py
@@ -234,7 +234,14 @@ def install(self):
                     # of the master branch
                     if len(role_versions) > 0:
                         loose_versions = [LooseVersion(a.get('name', None)) for a in role_versions]
-                        loose_versions.sort()
+                        try:
+                            loose_versions.sort()
+                        except TypeError:
+                            raise AnsibleError(
+                                'Unable to compare role versions (%s) to determine the most recent version due to incompatible version formats. '
+                                'Please contact the role author to resolve versioning conflicts, or specify an explicit role version to '
+                                'install.' % ', '.join([v.vstring for v in loose_versions])
+                            )
                         self.version = str(loose_versions[-1])
                     elif role_data.get('github_branch', None):
                         self.version = role_data['github_branch']


Test patch: