Patchwork [1,of,3] util: add versiontuple() for returning parsed version information

login
register
mail settings
Submitter Gregory Szorc
Date Nov. 24, 2015, 11:19 p.m.
Message ID <9a38b970471b77e566d2.1448407148@gps-mbp.local>
Download mbox | patch
Permalink /patch/11627/
State Accepted
Delegated to: Yuya Nishihara
Headers show

Comments

Gregory Szorc - Nov. 24, 2015, 11:19 p.m.
# HG changeset patch
# User Gregory Szorc <gregory.szorc@gmail.com>
# Date 1448403831 28800
#      Tue Nov 24 14:23:51 2015 -0800
# Node ID 9a38b970471b77e566d28f95643d9d21213f97a8
# Parent  df9b73d2d444ae82fe8d3fe6cf682a93b2c4a7ef
util: add versiontuple() for returning parsed version information

We have similar code in dispatch.py. Another consumer is about to be
created, so establish a generic function in an accessible location.
Yuya Nishihara - Nov. 27, 2015, 1:46 p.m.
On Tue, 24 Nov 2015 15:19:08 -0800, Gregory Szorc wrote:
> # HG changeset patch
> # User Gregory Szorc <gregory.szorc@gmail.com>
> # Date 1448403831 28800
> #      Tue Nov 24 14:23:51 2015 -0800
> # Node ID 9a38b970471b77e566d28f95643d9d21213f97a8
> # Parent  df9b73d2d444ae82fe8d3fe6cf682a93b2c4a7ef
> util: add versiontuple() for returning parsed version information
> 
> We have similar code in dispatch.py. Another consumer is about to be
> created, so establish a generic function in an accessible location.

Pushing the first two to the clowncopter, thanks.

I've modified this patch as follows:

> +def versiontuple(v=None, n=4):
> +    """Parses a Mercurial version string into an N-tuple.
> +
> +    The version string to be parsed is specified with the ``v`` argument.
> +    If it isn't defined, the current Mercurial version string will be parsed.
> +
> +    ``n`` can be 2, 3, or 4. Here is how some version strings map to
> +    returned values:
> +
> +    3.6.1+190-df9b73d2d444
> +      2: (3, 6)
> +      3: (3, 6, 1)
> +      4: (3, 6, 1, '190-df9b73d2d444')
> +
> +    3.6
> +      2: (3, 6)
> +      3: (3, 6, None)
> +      4: (3, 6, None, None)

Rewritten as doctest (and found bug :-)

> +    parts = v.split('+')
> +    if len(parts) == 1:
> +        vparts, extra = parts[0], None
> +    else:
> +        vparts, extra = parts

Unchanged. I'm not sure if ValueError is intended here for len(parts) > 2.

> +    vints = []
> +    for i in vparts.split('.'):
> +        try:
> +            vints.append(int(i))
> +        except ValueError:
> +            break
> +    # (3, 6) -> (3, 6, None)
> +    while len(vints) < 3:
> +        vints.append(None)

Unchanged, but filling None won't always be useful. For example,
"(2, 0, 0) > (2, 0, None)" works but is obscure than "(2, 0, 0) > (2, 0)".
Also it crashes on Python 3.

> +    # In case there are extra fields.
> +    vints = vints[0:3]

Removed. Extra fields won't cause trouble.

> +    if n == 2:
> +        return (vints[0], vints[1])
> +    if n == 3:
> +        return (vints[0], vints[1], vints[2])
> +    if n == 4:
> +        return (vints[0], vints[2], vints[3], extra)

Fixed index error on n == 4 case.

FWIW, what should we do if n == 1 or n == 5?
Instead of the n argument, I prefer versiontuple()[:n] as it is simple and
explicit.

Regards,
Yuya Nishihara - Dec. 2, 2015, 2:35 p.m.
On Fri, 27 Nov 2015 22:46:39 +0900, Yuya Nishihara wrote:
> On Tue, 24 Nov 2015 15:19:08 -0800, Gregory Szorc wrote:
> > # HG changeset patch
> > # User Gregory Szorc <gregory.szorc@gmail.com>
> > # Date 1448403831 28800
> > #      Tue Nov 24 14:23:51 2015 -0800
> > # Node ID 9a38b970471b77e566d28f95643d9d21213f97a8
> > # Parent  df9b73d2d444ae82fe8d3fe6cf682a93b2c4a7ef
> > util: add versiontuple() for returning parsed version information

> > +    parts = v.split('+')
> > +    if len(parts) == 1:
> > +        vparts, extra = parts[0], None
> > +    else:
> > +        vparts, extra = parts
> 
> Unchanged. I'm not sure if ValueError is intended here for len(parts) > 2.

Oops, I found a version could be "3.6.1+306-fc3381d38833+20151202" if working
copy is dirty. I'll push the fixed version soon.

Patch

diff --git a/mercurial/util.py b/mercurial/util.py
--- a/mercurial/util.py
+++ b/mercurial/util.py
@@ -335,8 +335,54 @@  def version():
         return __version__.version
     except ImportError:
         return 'unknown'
 
+def versiontuple(v=None, n=4):
+    """Parses a Mercurial version string into an N-tuple.
+
+    The version string to be parsed is specified with the ``v`` argument.
+    If it isn't defined, the current Mercurial version string will be parsed.
+
+    ``n`` can be 2, 3, or 4. Here is how some version strings map to
+    returned values:
+
+    3.6.1+190-df9b73d2d444
+      2: (3, 6)
+      3: (3, 6, 1)
+      4: (3, 6, 1, '190-df9b73d2d444')
+
+    3.6
+      2: (3, 6)
+      3: (3, 6, None)
+      4: (3, 6, None, None)
+    """
+    if not v:
+        v = version()
+    parts = v.split('+')
+    if len(parts) == 1:
+        vparts, extra = parts[0], None
+    else:
+        vparts, extra = parts
+
+    vints = []
+    for i in vparts.split('.'):
+        try:
+            vints.append(int(i))
+        except ValueError:
+            break
+    # (3, 6) -> (3, 6, None)
+    while len(vints) < 3:
+        vints.append(None)
+    # In case there are extra fields.
+    vints = vints[0:3]
+
+    if n == 2:
+        return (vints[0], vints[1])
+    if n == 3:
+        return (vints[0], vints[1], vints[2])
+    if n == 4:
+        return (vints[0], vints[2], vints[3], extra)
+
 # used by parsedate
 defaultdateformats = (
     '%Y-%m-%d %H:%M:%S',
     '%Y-%m-%d %I:%M:%S%p',