Created
August 7, 2015 00:40
-
-
Save kentfredric/cfd5a593d3d90d87ae11 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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