def test_doc_brag(self): # ensure that capabilities we brag about in # docs/VERSION_COMPARISON.md are actually there self.assertEqual(VersionCompare('1.2.3alpha4', '1.2.3~a4'), 0) self.assertEqual(VersionCompare('1.2.3alpha4', '1.2.3.a4'), 0) self.assertEqual(VersionCompare('1.2.3alpha4', '1.2.3'), -1) self.assertEqual(VersionCompare('1.2.3~a4', '1.2.3'), -1) self.assertEqual(VersionCompare('1.2.3.a4', '1.2.3'), -1)
def test_equality(self): self.assertEqual(VersionCompare('0', '0'), 0) self.assertEqual(VersionCompare('0a', '0a'), 0) self.assertEqual(VersionCompare('0.a', '0.a'), 0) self.assertEqual(VersionCompare('1a1', '1a1'), 0) self.assertEqual(VersionCompare('1alpha1', '1alpha1'), 0) self.assertEqual(VersionCompare('a', 'a'), 0) self.assertEqual(VersionCompare('foo', 'foo'), 0) self.assertEqual(VersionCompare('1.2.3', '1.2.3'), 0) self.assertEqual(VersionCompare('hello.world', 'hello.world'), 0)
def Parse(self, path): packages = [] for moduledir in os.listdir(path): modulepath = os.path.join(path, moduledir) maxversion = None for versiondir in os.listdir(modulepath): if versiondir == 'preferred-versions': continue if maxversion is None or VersionCompare( versiondir, maxversion) > 0: maxversion = versiondir pkg = Package() # XXX: parse .cabal file pkg.name = moduledir pkg.version = maxversion pkg.homepage = 'http://hackage.haskell.org/package/' + moduledir packages.append(pkg) return packages
def IsBetterVersion(version, maxversion): # if we have no best version, take any if maxversion is None: return True # prefer version without 9999 to version with 9999 if version.endswith('9999') == maxversion.endswith('9999'): return VersionCompare(version, maxversion) > 0 return not version.endswith('9999')
def Parse(self, path): packages = [] for moduledir in os.listdir(path): modulepath = os.path.join(path, moduledir) cabalpath = None maxversion = None for versiondir in os.listdir(modulepath): if versiondir == 'preferred-versions': continue if maxversion is None or VersionCompare(versiondir, maxversion) > 0: maxversion = versiondir cabalpath = os.path.join(path, moduledir, maxversion, moduledir + '.cabal') pkg = Package() pkg.name = moduledir pkg.version = maxversion pkg.homepage = 'http://hackage.haskell.org/package/' + moduledir cabaldata = self.ParseCabal(cabalpath) if cabaldata['name'] == pkg.name and VersionCompare(cabaldata['version'], pkg.version) == 0: if 'synopsis' in cabaldata and cabaldata['synopsis']: pkg.comment = cabaldata['synopsis'].strip() # XXX: leave for later, need extra postprocessing, too much obfuscation #if 'maintainer' in cabaldata: # pkg.maintainers = GetMaintainers(cabaldata['maintainer']) if 'license' in cabaldata: pkg.licenses = [cabaldata['license']] if 'homepage' in cabaldata and (cabaldata['homepage'].startswith('http://') or cabaldata['homepage'].startswith('https://')): pkg.homepage = cabaldata['homepage'] if 'category' in cabaldata: pkg.category = cabaldata['category'] else: print('WARNING: cabal data sanity check failed for {}, ignoring cabal data'.format(cabalpath), file=sys.stderr) packages.append(pkg) return packages
def test_miscomparation3(self): # github issue #138 self.assertEqual(VersionCompare('1.7.5~a1', '1.7.5a1'), 0) self.assertEqual(VersionCompare('1.7.5a1', '1.7.5~a1'), 0) self.assertEqual(VersionCompare('1.7.5~a1', '1.7.5-a1'), 0) self.assertEqual(VersionCompare('1.7.5-a1', '1.7.5~a1'), 0) self.assertEqual(VersionCompare('1.7.5a1', '1.7.5-a1'), 0) self.assertEqual(VersionCompare('1.7.5-a1', '1.7.5a1'), 0)
def AggregateBySameVersion(packages): current = None for package in packages: if current is None: current = [package] elif VersionCompare(current[0].version, package.version) == 0: current.append(package) else: yield current current = [package] if current is not None: yield current
def PackagesetToSummaries(packages): summary = {} state_by_repo = {} families = set() for package in packages: families.add(package.family) if package.repo not in state_by_repo: state_by_repo[package.repo] = { 'has_outdated': False, 'bestpackage': None, 'count': 0 } if package.versionclass == PackageVersionClass.outdated: state_by_repo[package.repo]['has_outdated'] = True, if state_by_repo[ package.repo]['bestpackage'] is None or VersionCompare( package.version, state_by_repo[package.repo]['bestpackage'].version) > 0: state_by_repo[package.repo]['bestpackage'] = package state_by_repo[package.repo]['count'] += 1 for repo, state in state_by_repo.items(): resulting_class = None # XXX: lonely ignored package is currently lonely; should it be ignored instead? if state['bestpackage'].versionclass == PackageVersionClass.outdated: resulting_class = RepositoryVersionClass.outdated elif len(families) == 1: resulting_class = RepositoryVersionClass.lonely elif state['bestpackage'].versionclass == PackageVersionClass.newest: if state['has_outdated']: resulting_class = RepositoryVersionClass.mixed else: resulting_class = RepositoryVersionClass.newest elif state['bestpackage'].versionclass == PackageVersionClass.ignored: resulting_class = RepositoryVersionClass.ignored summary[repo] = { 'version': state['bestpackage'].version, 'bestpackage': state['bestpackage'], 'versionclass': resulting_class, 'numpackages': state['count'] } return summary
def FillPackagesetVersions(packages): versions = set() families = set() for package in packages: if not package.ignoreversion: versions.add(package.version) families.add(package.family) bestversion = None for version in versions: if bestversion is None or VersionCompare(version, bestversion) > 0: bestversion = version for package in packages: result = VersionCompare(package.version, bestversion) if bestversion is not None else 1 if result > 0: package.versionclass = PackageVersionClass.ignored elif result == 0: # XXX: if len(families) == 1 -> PackageVersionClass.unique package.versionclass = PackageVersionClass.newest else: package.versionclass = PackageVersionClass.outdated
def test_letter_component(self): self.assertEqual(VersionCompare('1.0.a', '1.0'), -1) self.assertEqual(VersionCompare('1.0', '1.0.a'), 1) self.assertEqual(VersionCompare('1.0.a', '1.0.b'), -1) self.assertEqual(VersionCompare('1.0.b', '1.0.a'), 1) self.assertEqual(VersionCompare('1.0.a', '1.0.0'), -1) self.assertEqual(VersionCompare('1.0.0', '1.0.a'), 1)
def VersionCompare(self, other): self_metaorder = PackageFlags.GetMetaorder(self.flags) other_metaorder = PackageFlags.GetMetaorder(other.flags) if self_metaorder < other_metaorder: return -1 if self_metaorder > other_metaorder: return 1 return VersionCompare( self.version, other.version, ((self.flags & PackageFlags.p_is_patch) and P_IS_PATCH_LEFT) | ((other.flags & PackageFlags.p_is_patch) and P_IS_PATCH_RIGHT) | ((self.flags & PackageFlags.any_is_patch) and ANY_IS_PATCH_LEFT) | ((other.flags & PackageFlags.any_is_patch) and ANY_IS_PATCH_RIGHT) )
def Parse(self, path): result = {} # note that we actually parse database prepared by # fetcher, not the file we've downloaded with open(path, 'r', encoding='utf-8') as jsonfile: for entry in json.load(jsonfile)['releases']: pkg = Package() pkg.name = entry['name'] pkg.version = entry['version'] if not pkg.name or not pkg.version: continue homepage = entry.get('homepage') summary = entry.get('summary') description = entry.get('description') #submitter = entry.get('submitter') #download = entry.get('download') license_ = entry.get('license') if homepage: pkg.homepage = homepage if summary: pkg.comment = summary elif description: pkg.comment = description # multiline if license_: pkg.licenses = [license_] # unfiltered garbage #if submitter: # pkg.maintainers = [submitter + '@freshcode'] # ignore for now, may contain download page urls instead of file urls #if download # pkg.downloads = [download] if pkg.name not in result or VersionCompare( pkg.version, result[pkg.name].version) > 0: result[pkg.name] = pkg return result.values()
def Fetch(self, statepath, update=True, logger=NoopLogger()): if os.path.isfile(statepath) and not update: logger.Log('no update requested, skipping') return state = {} if os.path.isfile(statepath): with open(statepath, 'r', encoding='utf-8') as oldstatefile: state = json.load(oldstatefile) logger.Log('loaded old state, {} entries'.format(len(state))) else: logger.Log('starting with empty state') newdata = json.loads(Get(self.url).text) # add new entries in reversed order, oldest first so newest # have higher priority; may also compare versions here for entry in newdata['releases']: if 'name' not in entry: logger.Log('skipping entry with no name') continue if entry['name'] in state: oldentry = state[entry['name']] if VersionCompare(entry['version'], oldentry['version']) > 0: logger.Log( 'replacing entry "{}", version changed {} -> {}'. format(entry['name'], oldentry['version'], entry['version'])) state[entry['name']] = entry else: logger.Log('adding entry "{}", version {}'.format( entry['name'], entry['version'])) state[entry['name']] = entry temppath = statepath + '.tmp' with open(temppath, 'w', encoding='utf-8') as newstatefile: json.dump(state, newstatefile) os.replace(temppath, statepath) logger.Log('saved new state, {} entries'.format(len(state)))
def test_simple_comparisons(self): self.assertEqual(VersionCompare('0.0.0', '0.0.1'), -1) self.assertEqual(VersionCompare('0.0.1', '0.0.0'), 1) self.assertEqual(VersionCompare('0.0.1', '0.0.2'), -1) self.assertEqual(VersionCompare('0.0.2', '0.0.1'), 1) self.assertEqual(VersionCompare('0.0.2', '0.1.0'), -1) self.assertEqual(VersionCompare('0.1.0', '0.0.2'), 1) self.assertEqual(VersionCompare('0.0.2', '0.0.10'), -1) self.assertEqual(VersionCompare('0.0.10', '0.0.2'), 1) self.assertEqual(VersionCompare('0.0.10', '0.1.0'), -1) self.assertEqual(VersionCompare('0.1.0', '0.0.10'), 1) self.assertEqual(VersionCompare('0.1.0', '0.1.1'), -1) self.assertEqual(VersionCompare('0.1.1', '0.1.0'), 1) self.assertEqual(VersionCompare('0.1.1', '1.0.0'), -1) self.assertEqual(VersionCompare('1.0.0', '0.1.1'), 1) self.assertEqual(VersionCompare('1.0.0', '10.0.0'), -1) self.assertEqual(VersionCompare('10.0.0', '1.0.0'), 1) self.assertEqual(VersionCompare('10.0.0', '100.0.0'), -1) self.assertEqual(VersionCompare('100.0.0', '10.0.0'), 1) self.assertEqual(VersionCompare('10.10000.10000', '11.0.0'), -1) self.assertEqual(VersionCompare('11.0.0', '10.10000.10000'), 1)
def test_leading_zeroes(self): self.assertEqual(VersionCompare('00100.00100', '100.100'), 0) self.assertEqual(VersionCompare('100.100', '00100.00100'), 0) self.assertEqual(VersionCompare('0', '00000000'), 0) self.assertEqual(VersionCompare('00000000', '0'), 0)
def test_different_number_of_components(self): self.assertEqual(VersionCompare('1', '1.0'), 0) self.assertEqual(VersionCompare('1.0', '1'), 0) self.assertEqual(VersionCompare('1', '1.0.0'), 0) self.assertEqual(VersionCompare('1.0.0', '1'), 0)
def test_case_insensitivity(self): self.assertEqual(VersionCompare('1.0A', '1.0a'), 0) self.assertEqual(VersionCompare('1.0ALPHA', '1.0alpha'), 0)
def test_miscomparation2(self): # github issue #16 self.assertEqual(VersionCompare('4.89', '4.90.f'), -1) self.assertEqual(VersionCompare('4.90.f', '4.49'), 1)
def test_letter_addendum(self): self.assertEqual(VersionCompare('1.0', '1.0a'), -1) self.assertEqual(VersionCompare('1.0a', '1.0'), 1) self.assertEqual(VersionCompare('1.0a', '1.0b'), -1) self.assertEqual(VersionCompare('1.0b', '1.0a'), 1)
def test_letter_vs_number(self): self.assertEqual(VersionCompare('a', '0'), -1) self.assertEqual(VersionCompare('0', 'a'), 1)
def test_prerelease_sequence(self): self.assertEqual(VersionCompare('1.0.alpha1', '1.0.alpha2'), -1) self.assertEqual(VersionCompare('1.0.alpha2', '1.0.beta1'), -1) self.assertEqual(VersionCompare('1.0.beta1', '1.0.beta2'), -1) self.assertEqual(VersionCompare('1.0.beta2', '1.0.rc1'), -1) self.assertEqual(VersionCompare('1.0.beta2', '1.0.pre1'), -1) self.assertEqual(VersionCompare('1.0.rc2', '1.0'), -1) self.assertEqual(VersionCompare('1.0.pre2', '1.0'), -1) # XXX: is rc/pre order defined? self.assertEqual(VersionCompare('1.0alpha1', '1.0alpha2'), -1) self.assertEqual(VersionCompare('1.0alpha2', '1.0beta1'), -1) self.assertEqual(VersionCompare('1.0beta1', '1.0beta2'), -1) self.assertEqual(VersionCompare('1.0beta2', '1.0rc1'), -1) self.assertEqual(VersionCompare('1.0beta2', '1.0pre1'), -1) self.assertEqual(VersionCompare('1.0rc2', '1.0'), -1) self.assertEqual(VersionCompare('1.0pre2', '1.0'), -1)
def test_non_dot_separator(self): self.assertEqual(VersionCompare('1.0.beta1', '1.0_beta1'), 0)
def test_specific1(self): self.assertEqual(VersionCompare('1.0r1', '1.0_RC1'), 0)
def test_case_is_ignored(self): self.assertEqual(VersionCompare('1a', '1ALPHA'), 0) self.assertEqual(VersionCompare('1A1', '1alpha1'), 0)
def test_long_number(self): self.assertEqual(VersionCompare('20160101', '20160102'), -1) self.assertEqual(VersionCompare('20160102', '20160101'), 1)
def test_empty(self): self.assertEqual(VersionCompare('', '0'), 0) self.assertEqual(VersionCompare('', '1'), -1)
def test_even_longer_numver(self): self.assertEqual( VersionCompare('9999999999999999', '10000000000000000'), -1) self.assertEqual( VersionCompare('10000000000000000', '9999999999999999'), 1)
def test_garbage(self): self.assertEqual(VersionCompare('......-----~~~~~!!!', '0.0.0.0'), 0) self.assertEqual(VersionCompare('.-~1~-.-~2~-.', '1.2'), 0)
def test_complex_cases(self): self.assertEqual(VersionCompare('1.0beta1', '1.0.beta1'), 0) self.assertEqual(VersionCompare('1.0beta1', '1.0.b1'), 0) self.assertEqual(VersionCompare('1.0.beta1', '1.0.b1'), 0) self.assertEqual(VersionCompare('1.0.beta1', '1.0.b1'), 0) self.assertEqual(VersionCompare('1.0alpha1', '1.0'), -1) self.assertEqual(VersionCompare('1.0', '1.0alpha1'), 1) self.assertEqual(VersionCompare('1.0beta1', '1.0'), -1) self.assertEqual(VersionCompare('1.0', '1.0beta1'), 1) self.assertEqual(VersionCompare('1.0pre1', '1.0'), -1) self.assertEqual(VersionCompare('1.0', '1.0pre1'), 1) self.assertEqual(VersionCompare('1.0rc1', '1.0'), -1) self.assertEqual(VersionCompare('1.0', '1.0rc1'), 1)
def test_miscomparation1(self): # github issue #16 self.assertEqual(VersionCompare('1.4c', '1.4e'), -1) self.assertEqual(VersionCompare('1.4e', '1.4c'), 1)