Repository: ekalinin/nodeenv

Issue:
nodeenv --list is raising TypeError
When installed globally with `sudo pip install nodeenv`, `nodeenv --list` raises an exception.

This is due to `pkg_resources.parse_version` method having different return types in older versions. 

![nodeenv_bug](https://cloud.githubusercontent.com/assets/6031925/6599860/d253a2b0-c814-11e4-94d6-9a9d79cdf27c.png)


Possible hotfix solutions are:
* write a helper function to parse strings,
* write our own Version type :-1: 

But atm I'm working on finding out which version of `setuptools` introduced the newer (currently used in master) Version type that parses version strings and rely on it if `setuptools` is >= that version.

I'll combine that with one of two approaches:
* the raw string representation we get with regex pattern. However ascending sorting will be `0.1.0, 0.10.0, 0.9.0`,
*  the before mentioned helper function


I think broken ascending sort is acceptable as a hotfix.

PR will be submitted soon-ish. :8ball: 

Discussion:

Patch:
diff --git a/nodeenv.py b/nodeenv.py
index c00a75c..ec9e358 100644
--- a/nodeenv.py
+++ b/nodeenv.py
@@ -36,13 +36,15 @@ except ImportError:  # pragma: no cover (py3 only)
 
 from pkg_resources import parse_version
 
-
 nodeenv_version = '0.13.0'
 
 join = os.path.join
 abspath = os.path.abspath
 src_domain = "nodejs.org"
 
+is_PY3 = sys.version_info[0] == 3
+if is_PY3:
+    from functools import cmp_to_key
 # ---------------------------------------------------------
 # Utils
 
@@ -711,20 +713,58 @@ def create_environment(env_dir, opt):
         callit(['rm -rf', pipes.quote(src_dir)], opt.verbose, True, env_dir)
 
 
+class GetsAHrefs(HTMLParser):
+    def __init__(self):
+        # Old style class in py2 :(
+        HTMLParser.__init__(self)
+        self.hrefs = []
+
+    def handle_starttag(self, tag, attrs):
+        if tag == 'a':
+            self.hrefs.append(dict(attrs).get('href', ''))
+
 VERSION_RE = re.compile('\d+\.\d+\.\d+')
 
 
+def _py2_cmp(a, b):
+    # -1 = a < b, 0 = eq, 1 = a > b
+    return (a > b) - (a < b)
+
+
+def compare_versions(version, other_version):
+    version_tuple = version.split('.')
+    other_tuple = other_version.split('.')
+
+    version_length = len(version_tuple)
+    other_length = len(other_tuple)
+    version_dots = min(version_length, other_length)
+
+    for i in range(version_dots):
+        a = int(version_tuple[i])
+        b = int(other_tuple[i])
+        cmp_value = _py2_cmp(a, b)
+        if cmp_value != 0:
+            return cmp_value
+
+    return _py2_cmp(version_length, other_length)
+
+
 def get_node_versions():
     response = urlopen('https://{0}/dist'.format(src_domain))
     href_parser = GetsAHrefs()
     href_parser.feed(response.read().decode('UTF-8'))
+
     versions = set(
         VERSION_RE.search(href).group()
         for href in href_parser.hrefs
         if VERSION_RE.search(href)
     )
-    sorted_versions = sorted([parse_version(version) for version in versions])
-    return [v.public for v in sorted_versions]
+    if is_PY3:
+        key_compare = cmp_to_key(compare_versions)
+        versions = sorted(versions, key=key_compare)
+    else:
+        versions = sorted(versions, cmp=compare_versions)
+    return versions
 
 
 def print_node_versions():
@@ -739,17 +779,6 @@ def print_node_versions():
         logger.info('\t'.join(chunk))
 
 
-class GetsAHrefs(HTMLParser):
-    def __init__(self):
-        # Old style class in py2 :(
-        HTMLParser.__init__(self)
-        self.hrefs = []
-
-    def handle_starttag(self, tag, attrs):
-        if tag == 'a':
-            self.hrefs.append(dict(attrs).get('href', ''))
-
-
 def get_last_stable_node_version():
     """
     Return last stable node.js version


Test patch:
diff --git a/tests/nodeenv_test.py b/tests/nodeenv_test.py
index 8c8b163..f13b219 100644
--- a/tests/nodeenv_test.py
+++ b/tests/nodeenv_test.py
@@ -14,6 +14,17 @@ import nodeenv
 HERE = os.path.abspath(os.path.dirname(__file__))
 
 
+def test_compare_versions():
+    assert nodeenv.compare_versions('1', '2') == -1
+    assert nodeenv.compare_versions('1', '2') == -1
+    assert nodeenv.compare_versions('0.1', '0.2') == -1
+    assert nodeenv.compare_versions('0.9', '0.10') == -1
+    assert nodeenv.compare_versions('0.2', '0.2.1') == -1
+    assert nodeenv.compare_versions('0.2.1', '0.2.10') == -1
+    assert nodeenv.compare_versions('0.2.9', '0.2.10') == -1
+    assert nodeenv.compare_versions('0.2.1', '0.3') == -1
+
+
 def test_gets_a_hrefs_trivial():
     parser = nodeenv.GetsAHrefs()
     parser.feed('')