Skip to content

Instantly share code, notes, and snippets.

@kentfredric
Created August 7, 2015 00:40
Show Gist options
  • Save kentfredric/cfd5a593d3d90d87ae11 to your computer and use it in GitHub Desktop.
Save kentfredric/cfd5a593d3d90d87ae11 to your computer and use it in GitHub Desktop.
import re;
from itertools import izip_longest
class CPANVersion (object):
__version_chunks = None
__original_version_string = None
__version_string = None
__alpha = None
__numified_version = None
__normalized_version = None
def __init__(self, original_version_string):
self.__original_version_string = original_version_string
self.__version_string = original_version_string
if vstr_is_alpha(original_version_string):
self.__alpha = True
self.__version_string = vstr_strip_alpha(original_version_string)
self.__version_chunks = parse_version_chunks(self.__version_string)
if ( vstr_is_float(self.__version_string )):
self.__numified_version = self.__version_string
else:
self.__numified_version = numify_chunks(self.__version_chunks)
self.__normalized_version = normalize_chunks(self.__version_chunks)
def is_v_multidot(self):
return vstr_is_v_multidot(self.__version_string)
def is_multidot(self):
return vstr_is_multidot(self.__version_string)
def is_float(self):
return vstr_is_float(self.__version_string)
def is_alpha(self):
return self.__alpha
def is_valid(self):
return self.is_v_multidot() or self.is_multidot() or self.is_float()
def gentoo_version(self):
if self.is_alpha():
return "%s_rc" % self.__normalized_version
return self.__normalized_version
def __cmp__(self, other):
version_zip = izip_longest( self.__version_chunks, other.__version_chunks, fillvalue = 0 )
for pair in version_zip:
if pair[0] < pair[1]:
return -1
if pair[0] > pair[1]:
return 1
return 0
def __str__(self):
return self.gentoo_version()
# Utility functions here
def vstr_parse_v_multidot(version_string):
""" Parses v1.2.3 forms into an array
character[0] assumed to be a "v" if this function is called.
"""
return vstr_parse_multidot(version_string[1:])
def vstr_parse_multidot(version_string):
""" Parses 1.2.3 forms into an array
assumed sanitization is already done and a split is safe
"""
return version_string.split('.')
def right_zero_pad(version_chunk):
""" Expand floating-sourced versions to 3 places for chunk representation
1 -> 100
01 -> 010
001 -> 001
"""
return version_chunk.ljust(3, "0")
def left_zero_pad(version_chunk):
""" Expand normal-sourced versions to 3 places for numifiable representation
1 -> 001
10 -> 010
100 -> 100
"""
return version_chunk.rjust(3, "0")
def left_zero_trim(version_chunk):
""" Remove extraneous leading 0's from a normal-sourced (or padded) version.
100 -> 100
10 -> 10
010 -> 10
1 -> 1
01 -> 1
001 -> 1
0 -> 0 # IMPORTANT
00 -> 0
000 -> 0
"""
chunk = version_chunk.lstrip("0")
# 000 -> "" normally, emit 0 instead.
if len(chunk) > 0:
return chunk
return "0"
def vstr_parse_float(version_string):
""" Split a x.yyyzzz version into [x, yyy, zzz ]
"""
head,sep,tail = version_string.partition('.');
version_parts = [head];
# Split the tail into chunks <= 3
# Pad the ones <3 long with trailing 0's
for i in xrange(0, len(tail), 3):
# Each chunk is assumed to be the left-most digits of 3, and the missing digits
# are replacable with 0's
# -> 0.[01] -> 0.[010]
version_parts.append(right_zero_pad(tail[i:i+3]))
return version_parts
def numify_chunks(version_chunks):
""" Converts a chunk sequence into a floating-point-like string
by padding each to at least 3 places and concatenating them
NOTE: This can be weird if you have [1, 1024 ]
because it will numify as [1, 102, 4]
SOLUTION: Don't compare numified versions, use __cmp__
"""
chunks = [version_chunks[0]]
if len(version_chunks) > 1:
chunks.append( "".join( map(left_zero_pad, version_chunks[1:] )) )
return ".".join(chunks)
def normalize_chunks(version_chunks):
""" Converts a chunk sequence into a dotted-decimal string
with at least 3 decimal parts ( 2-dots )
Any leading source '0's are trimmed, so:
v1.002.003 -> [1, 002, 003] -> 1.2.3
"""
version_parts = map(left_zero_trim, version_chunks)
while len(version_parts) < 3:
version_parts.append("0")
return ".".join(version_parts)
def vstr_is_v_multidot(version_string):
return re.match(r'^v\d+(\.\d+)*$', version_string)
def vstr_is_multidot(version_string):
return re.match(r'^\d+\.\d+(\.\d+)+$', version_string)
def vstr_is_float(version_string):
return re.match(r'^\d+(\.\d+)?$', version_string)
def vstr_is_alpha(version_string):
return version_string.count('_') > 0
def vstr_strip_alpha(version_string):
return version_string.replace("_","")
def parse_version_chunks(version_string):
if vstr_is_v_multidot(version_string):
return vstr_parse_v_multidot(version_string)
elif vstr_is_multidot(version_string):
return vstr_parse_multidot(version_string)
elif vstr_is_float(version_string):
return vstr_parse_float(version_string)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment