def determine_download_version(pkg_spec, package): version_spec = pkg_spec.package_version logger.info('Initial version spec: %s', version_spec) if version_spec is None: logger.info("Version not specified, using latest (%s)", package.latest) version_spec = package.latest # the python node-semver package doesn't work well with unicode, convert arguments to strings. pkg_versions = [str(v) for v in package.info['versions'].keys()] filtered_pkg_versions = [ v for v in pkg_versions if lte(v, package.latest, False) ] logger.info('Finding version (%s) up to latest(%s)', version_spec, package.latest) download_version = max_satisfying(filtered_pkg_versions, str(version_spec)) if download_version is None: logger.info( 'Version (%s) not found up to latest < latest(%s) trying all.', version_spec, package.latest) download_version = max_satisfying(pkg_versions, str(version_spec)) return download_version
def add_required_version(self, pkg_version): version_spec = pkg_version logger.debug('Initial version spec: %s', version_spec) if version_spec is None: logger.debug("Version not specified, using latest (%s)", self.latest) version_spec = self.latest # the python node-semver package doesn't work well with unicode, convert arguments to strings. pkg_versions = [str(v) for v in self.info['versions'].keys()] filtered_pkg_versions = [ v for v in pkg_versions if lte(v, self.latest, False) ] logger.debug('Finding version (%s) up to latest(%s)', version_spec, self.latest) download_version = max_satisfying(filtered_pkg_versions, str(version_spec)) if download_version is None: logger.debug( 'Version (%s) not found up to latest < latest(%s) trying all.', version_spec, self.latest) download_version = max_satisfying(pkg_versions, str(version_spec)) if download_version in self.required_versions: raise PackageVersionAlreadyRequiredError() self.required_versions.add(download_version) return self.info['versions'][download_version]
def test_it(versions, range_, expect, loose, include_prerelease): from semver import max_satisfying if isinstance(expect, type) and issubclass(expect, Exception): with pytest.raises(expect): max_satisfying(versions, range_, loose, include_prerelease) else: assert max_satisfying(versions, range_, loose, include_prerelease) == expect
def custom_max_satisfying(candidates, query): if not query.startswith("~"): return max_satisfying(candidates, query, loose=True) else: normalized = defaultdict(list) for x in candidates: v = make_semver(x, loose=True) normalized["{}.{}.{}".format(v.major, v.minor, v.patch)].append(v) mv = max_satisfying(list(normalized), query) return sort(normalized[mv], loose=True)[-1].version
def extract_deps(bundles, log=None): """Extract the dependencies from the bundle and its sub-bundles.""" def _flatten(bundle): deps = [] if hasattr(bundle, 'npm'): deps.append(bundle.npm) for content in bundle.contents: if isinstance(content, BundleBase): deps.extend(_flatten(content)) return deps flatten_deps = [] for bundle in bundles: flatten_deps.extend(_flatten(bundle)) packages = defaultdict(list) for dep in flatten_deps: for pkg, version in dep.items(): packages[pkg].append(version) deps = {} for package, versions in packages.items(): deps[package] = semver.max_satisfying(versions, '*', True) if log and len(versions) > 1: log("Warn: {} version {} resolved to: {}".format( repr(package), versions, repr(deps[package]))) return deps
def extract_deps(bundles, log=None): """Extract the dependencies from the bundle and its sub-bundles.""" def _flatten(bundle): deps = [] if hasattr(bundle, 'npm'): deps.append(bundle.npm) for content in bundle.contents: if isinstance(content, BundleBase): deps.extend(_flatten(content)) return deps flatten_deps = [] for bundle in bundles: flatten_deps.extend(_flatten(bundle)) packages = defaultdict(list) for dep in flatten_deps: for pkg, version in dep.items(): packages[pkg].append(version) deps = {} for package, versions in packages.items(): deps[package] = semver.max_satisfying(versions, '*', True) if log and len(versions) > 1: log("Warn: {} version {} resolved to: {}" .format(repr(package), versions, repr(deps[package]))) return deps
def satisfying(list_versions, versionexpr, result): """ returns the maximum version that satisfies the expression if some version cannot be converted to loose SemVer, it is discarded with a msg This provides some workaround for failing comparisons like "2.1" not matching "<=2.1" """ from semver import SemVer, Range, max_satisfying version_range, loose, include_prerelease = _parse_versionexpr(versionexpr, result) # Check version range expression try: act_range = Range(version_range, loose) except ValueError: raise ConanException("version range expression '%s' is not valid" % version_range) # Validate all versions candidates = {} for v in list_versions: try: ver = SemVer(v, loose=loose) candidates[ver] = v except (ValueError, AttributeError): result.append("WARN: Version '%s' is not semver, cannot be compared with a range" % str(v)) # Search best matching version in range result = max_satisfying(candidates, act_range, loose=loose, include_prerelease=include_prerelease) return candidates.get(result)
def _resolve_version(self, version_range, local_found): version_range = version_range.replace(",", " ") versions = {Version(ref.version): ref for ref in local_found} sorted_versions = reversed(sorted(versions)) from semver import max_satisfying result = max_satisfying(sorted_versions, version_range, loose=True) return versions.get(result)
def get_best_version(self): versions = self.versions version_string = self.version_string return max_satisfying(versions=versions, range_=version_string, loose=True)
def git(repo_url, repo_version_spec, path): if len(repo_url.split('/')) != 5: return False log.info('Downloading from the repo: ' + str(repo_url) + ' [' + (str(repo_version_spec) if repo_version_spec else 'master') + ']') try: r = Repo.clone_from(repo_url, path) versions_repo = {} for tag in r.tags: v = str( tag.name.lstrip('ver.').lstrip('v.').lstrip('ver').lstrip('v')) versions_repo[v] = str(tag.name) versions_repo = collections.OrderedDict( sorted(versions_repo.items(), reverse=True)) if len(versions_repo): versions_repo_keys = list(versions_repo.keys()) find_version = versions_repo_keys[0] if repo_version_spec: find_version = max_satisfying(versions_repo_keys, repo_version_spec, loose=False) repo_tag = versions_repo[find_version] r.git.checkout(repo_tag) except Exception as e: log.danger(str(e)) return False return True
def resolve_dependencies(requested_services, service_defs, resolved_services): for dependency in requested_services: name = dependency['name'] req_version = dependency['version'] existing_version = resolved_services.get(name, None) if existing_version != None and satisfies(existing_version['tag'], req_version): continue service_def = service_defs.get(name, None) if service_def == None: raise Exception('No matching service definition found for ' + name) # get the hightest 'listed' version available_versions = [] for version in service_def['versions']: available_versions.append(version['tag']) highest_version = max_satisfying(available_versions, req_version) # check to see if it can be used status = '' usable_versions = [] if highest_version: try: status = release_plan_lut[name][highest_version] if status in CANNOT_RELEASE: print('{}: version {} has a release plan in {} state'.format(name, version['tag'], status)) for version in service_def['versions']: filter_version(name, version['tag'], usable_versions) highest_version = max_satisfying(usable_versions, req_version) except KeyError: print('{}: no release plan for version {}'.format(name, highest_version)) if highest_version == None: resolved_services.clear() if status in CANNOT_RELEASE: print('{}: no usable version found for {}'.format(name, req_version)) print('A matching version was found but release plan is in {} state'.format(status)) print('Available versions: {}'.format(available_versions)) print('Usable versions: {}'.format(usable_versions)) else: print('{}: no matching version found for {}'.format(name, req_version)) print('Available versions: {}'.format(available_versions)) else: print('{}: resolved {} to version {} from {}'.format( name, req_version, highest_version, available_versions)) resolved_version = flatten_service_version(service_def, highest_version) resolved_services[name] = resolved_version resolve_dependencies(resolved_version['dependencies'], service_defs, resolved_services)
def best_version(self): if not hasattr(self, '_best_version'): self._best_version = max_satisfying( self.versions, force_bytes(self.version), loose=True ) print('Best version: {}'.format(self._best_version)) return self._best_version
def best_version(self): if not hasattr(self, '_best_version'): self._best_version = max_satisfying(self.versions, force_bytes(self.version), loose=True) print 'Best version: {}'.format(self._best_version) return self._best_version
def _resolve_version_of_requires(self, artifacts, requires): """@requires expect a tuple with like (project name, version)""" project_name, version = requires if project_name in artifacts: # print('#####', requires, '->', artifacts[project_name]) return max_satisfying(artifacts[project_name], version, loose=False, include_prerelease=True)
def _check(self): try: package_info = self._read_package_file() except FileNotFoundError: return False versions = [package_info['version']] version_string = self.version_string return max_satisfying(versions=versions, range_=version_string, loose=True)
def get_local(cls, name, range_): versions = {} for path in PARTICLES_PATH: p_path = path / name if p_path.exists(): for v_path in p_path.iterdir(): versions[v_path.name] = v_path max_version = semver.max_satisfying(versions.keys(), range_, False) if max_version: meta = json.load((versions[max_version] / "particle.json").open()) return cls(meta, versions[max_version])
def get_best_replicated_version(version_range, replicated_channel): cursor = db.get().cursor() query = ('SELECT version ' 'FROM product_version_channel_release_history ' 'WHERE product = %s AND channel = %s') cursor.execute(query, ("replicated_v2", replicated_channel)) best_v = semver.max_satisfying(version_list_generator(cursor), version_range, loose=False) cursor.close() print('Best matching Replicated version for {}: {}'.format( version_range, best_v)) return best_v
def cmd_run(path): """ Runs an appliction. """ os.chdir(path) package = Path("./package.json") if not package.is_file(): raise Exception("Invalid package: no package.json file") package = json.load(package.open()) if "engines" not in package or package["engines"] == {}: raise Exception("Invalid package: no engines specified") r = requests.get("%s/index.json" % Particle.REPO) r.raise_for_status() remote_particles = r.json()["particles"] variables = {} for name, range_ in package["engines"].items(): p = Particle.get_local(name, range_) if not p: # if auto_fetch: if name in remote_particles: v = semver.max_satisfying(remote_particles[name], range_, False) if v: print("Downloading %s %s..." % (name, v)) p = Particle.fetch(name, v) else: print("Cannot satisfy %s (%s), aborting." % (name, range_)) sys.exit(1) else: print("No particle named %s exists, aborting." % name) sys.exit(1) variables["$" + name.upper().replace("-", "_")] = str(p.main) pattern = re.compile('|'.join(map(re.escape, variables.keys()))) if "lepton" not in package: raise Exception("Invalid package: no lepton key in particle.json") elif "run" not in package["lepton"]: raise Exception("Invalid package: no lepton.run key in particle.json") args = package["lepton"]["run"] args = pattern.sub(lambda x: variables[x.group()], args) args = shlex.split(args) print("Resulting command line: %r" % args) print("Current dir: %s" % os.getcwd()) os.execvp(args[0], args)
def choose_version_by_strategy(target_list, version_selection_strategy): """ Choose a specific version in the list, according to version selection strategy specified by the user """ [target_range, choosing] = version_selection_strategy if choosing == VersionChoosing.NEWEST: result = semver.max_satisfying(target_list, target_range, loose=True) else: # min_satisfying is not implemented by the library yet, using our own implementation result = semver_min_satisfying(target_list, target_range) return result
def _check(self): try: package_info = self._read_package_file() except FileNotFoundErrorException: return False if PY3: versions = [package_info['version']] version_string = self.version_string else: versions = [force_bytes(package_info['version'])] version_string = force_bytes(self.version_string) return max_satisfying(versions=versions, range_=version_string, loose=True)
def satisfying(list_versions, versionexpr, output): """ returns the maximum version that satisfies the expression if some version cannot be converted to loose SemVer, it is discarded with a msg This provides some woraround for failing comparisons like "2.1" not matching "<=2.1" """ from semver import SemVer, max_satisfying version_range = versionexpr.replace(",", " ") candidates = {} for v in list_versions: try: ver = SemVer(v, loose=True) candidates[ver] = v except (ValueError, AttributeError): output.warn("Version '%s' is not semver, cannot be compared with a range" % str(v)) result = max_satisfying(candidates, version_range, loose=True) return candidates.get(result)
def get_best_version(self): # PY3 # node-semver does a direct str() comparison which means # different things on PY2 and PY3 # Typecast to str in PY3 which is unicode and # bytes in PY2 which is str to fool node-semver if PY3: versions = self.versions version_string = self.version_string else: versions = [force_bytes(version) for version in self.versions] version_string = force_bytes(self.version_string) return max_satisfying(versions=versions, range_=version_string, loose=True)
def merge_deps(deps, bundles_deps): """Merge NPM dependencies.""" keys = ['dependencies', 'devDependencies', 'peerDependencies'] for k in keys: deps.setdefault(k, {}) if k in bundles_deps: target_deps = deps[k] source_deps = bundles_deps[k] for pkg, version in source_deps.items(): if pkg in target_deps: satisfying = max_satisfying([target_deps[pkg], version], '*', True) target_deps[pkg] = satisfying else: target_deps[pkg] = version return deps
def basic_test(self): from semver import max_satisfying result = max_satisfying(["1.1", "1.2", "1.3", "2.1"], "1.1", loose=True) self.assertEqual(result, "1.1") result = max_satisfying(["1.1", "1.2", "1.3", "2.1"], ">1.1", loose=True) self.assertEqual(result, "2.1") result = max_satisfying(["1.1", "1.2", "1.3", "2.1"], ">1.1 <2.1", loose=True) self.assertEqual(result, "1.3") result = max_satisfying(["1.1.1", "1.1.2", "1.2.1", "1.3", "2.1"], "<1.2", loose=True) self.assertEqual(result, "1.1.2") result = max_satisfying(["1.1.1", "1.1.2", "1.2.1", "1.3", "2.1"], "<1.2.1", loose=True) self.assertEqual(result, "1.1.2") result = max_satisfying(["1.1.1", "1.1.2", "1.2.1", "1.3", "2.1"], "<=1.2.1", loose=True) self.assertEqual(result, "1.2.1") result = max_satisfying(["1.6.1"], ">1.5.0,<1.6.8", loose=True) self.assertEqual(result, "1.6.1") # FIXME: Very annoying, this lib, will not find 1.2 for this range result = max_satisfying(["1.1.1", "1.1.2", "1.2", "1.2.1", "1.3", "2.1"], "<=1.2", loose=True) self.assertEqual(result, "1.1.2")
def get_release(user, repo_ver, name_re): try: name_reobj = re.compile(name_re) except re.error as e: return jsonify(message=f"bad regular expression: {e.msg}"), 400 repo_ver = repo_ver.split('@', 1) repo = repo_ver[0] if len(repo_ver) == 2: # versioned ver = repo_ver[1] all_releases, err = api_req( f"https://api.github.com/repos/{user}/{repo}/releases") if err: return err all_tags = [x['tag_name'] for x in all_releases] if ver in all_tags: # exact match tag = f"tags/{ver}" else: try: v = semver.max_satisfying(all_tags, ver) except Exception as e: return jsonify(message=f"error matching the tag: {e}"), 400 if v is None: return jsonify(message="no tag matched"), 404 tag = f"tags/{v}" else: tag = "latest" release, err = api_req( f"https://api.github.com/repos/{user}/{repo}/releases/{tag}") if err: return err if name_re == 'tar': return redirect(release['tarball_url']) if name_re == 'zip': return redirect(release['zipball_url']) assets = release['assets'] matched = [ x['browser_download_url'] for x in assets if name_reobj.search(x['name']) ] if len(matched) == 0: return jsonify(message="no file matched"), 404 matched.sort(key=len) return redirect(matched[0])
def pick_php_version(self, requested): selected = None if requested is None or requested is '': return self._ctx['PHP_VERSION'] # requested is coming from the composer.json file and is a unicode string type. # Since it's just a semver string, it shouldn't actually contain any unicode # characters. So it should be safe to turn it into an ASCII string translated_requirement = str(requested.replace('>=', '~>')) selected = max_satisfying(self._ctx['ALL_PHP_VERSIONS'], translated_requirement, loose=False) if selected is None: docs_link = 'http://docs.cloudfoundry.org/buildpacks/php/gsg-php-composer.html' warn_invalid_php_version(requested, self._ctx['PHP_DEFAULT'], docs_link) selected = self._ctx['PHP_DEFAULT'] return selected
def _resolve_product(self, product_name, product_version): response = requests.get('{0}/{1}'.format(NPM_REGISTRY_URL, product_name)) validate_http_status_code(response.status_code, self, product_name, product_version) response_payload = response.json() version = product_version if product_version == 'latest': version = response_payload['dist-tags']['latest'] if version is None: raise Exception('Version was not found for {0}:{1}'.format( product_name, product_version)) all_versions = list(response_payload['versions'].keys()) version = semver.max_satisfying(all_versions, version, loose=False) self._logger.info('Resolved npm package {0}@{1} to version {2}'.format( product_name, product_version, version)) download_url = response_payload['versions'][version]['dist']['tarball'] return Product(self, product_name, version, download_url)
def get_best_replicated_version(version_range, replicated_channel, scheduler=None): # we do not make scheduler-less releases past 2.37 if not scheduler and semver.ltr('2.37.99', version_range, loose=False): return get_current_replicated_version(replicated_channel, scheduler=None) cursor = db.get().cursor() product = 'replicated_v2' if not scheduler: query = ('SELECT version ' 'FROM product_version_channel_release_history ' 'WHERE product = %s AND channel = %s') cursor.execute(query, (product, replicated_channel)) if scheduler: # select the default if the scheduler row does not exist query = ('SELECT version ' 'FROM product_version_channel_release_history ' 'WHERE (product = %s OR product = %s) AND channel = %s') cursor.execute(query, ( product + '-' + scheduler, product, replicated_channel, )) best_v = semver.max_satisfying(version_list_generator(cursor), version_range, loose=False) cursor.close() print('Best matching Replicated version for {}: {}'.format( version_range, best_v), file=sys.stderr) return best_v
import logging from semver import make_semver, max_satisfying # logging.basicConfig(level=logging.DEBUG) print(make_semver('4.1.3.0', loose=True)) print(max_satisfying(['3.1.1', '4.1.1.0', '4.1.2.0', '4.1.3.0'], '>= 4.1.0 <5.0.0', loose=True)) print(max_satisfying(['3.1.1', '4.1.2.0', '4.1.1.0', '4.1.3.0'], '^4.1', loose=True))
def test_it(versions, range_, expect, loose): from semver import max_satisfying assert max_satisfying(versions, range_, loose) == expect
def choose_version(self, information, word, restriction=""): versions = [d["name"] for d in information.version(word)] return max_satisfying(versions, restriction, loose=True)
import semver cands = ["1.2.3-alpha.1"] print(semver.max_satisfying(cands, "~1.2.3")) print(semver.max_satisfying(cands, "~1.2.3", True)) print(semver.max_satisfying(cands, "~1.2.3-")) print(semver.max_satisfying(cands, "~1.2.3-", True)) # 1.2.3-a.111 (only this) print(semver.max_satisfying(cands, "~1.2.3-*")) print(semver.max_satisfying(cands, "~1.2.3-*", True))
def test_it(): from semver import max_satisfying assert max_satisfying(["2.4.3", "2.4.4", "2.5b", "3.0.1-b"], "~2", True) == "2.5b"
def satisfy(versions, range_, result): sat = max_satisfying(versions, range_, loose=True) assert result == sat, "%s != %s" % (result, sat)
def satisfy(versions, range_, result): if range_.startswith("~"): range_ = range_ + "-" sat = max_satisfying(versions, range_, loose=True) assert result == sat, "%s != %s" % (result, sat)
def test_it3(): from semver import max_satisfying assert max_satisfying(["2.5b", "v2010.07.06dev"], "~2", True) == "2.5b"
def test_it(op, wanted, cands): from semver import max_satisfying got = max_satisfying(cands, op, loose=True) assert got == wanted
def test_it2(): from semver import max_satisfying assert max_satisfying(["2b", "3.0.1-b"], "~2", True) == "2b"
def download_package(output_directory, spec, duplicate_download_preventer, force=False): """ Download the package specified into the output directory specified. """ logger.info('Processing %s', spec) pkg_spec = PackageSpec(spec) info = get_package_info(pkg_spec.registry_package_name) # if no version was specified, use the latest specified. latest = str(info['dist-tags']['latest']) next_version = str( info['dist-tags']['next']) if 'next' in info['dist-tags'] else None version_spec = pkg_spec.package_version logger.info('Initial version spec: %s', version_spec) if version_spec is None: logger.info("Version not specified, using latest (%s)", latest) version_spec = latest # the python node-semver package doesn't work well with unicode, convert arguments to strings. pkg_versions = [str(v) for v in info['versions'].keys()] filtered_pkg_versions = [v for v in pkg_versions if lte(v, latest, False)] logger.info('Finding version (%s) up to latest(%s)', version_spec, latest) download_version = max_satisfying(filtered_pkg_versions, str(version_spec)) if download_version is None: logger.info( 'Version (%s) not found up to latest < latest(%s) trying all.', version_spec, latest) download_version = max_satisfying(pkg_versions, str(version_spec)) if pkg_spec.registry_package_name in duplicate_download_preventer and download_version in duplicate_download_preventer[ pkg_spec.registry_package_name]: logger.info('Previously downloaded package: %s, version %s', pkg_spec.registry_package_name, download_version) return # get information about the version the user wanted. version_info = info['versions'][download_version] tarball_url = version_info['dist']['tarball'] expected_shasum = version_info['dist']['shasum'] # derive paths for storing the cached json info document and the tarball. Scoped packages are special. if pkg_spec.is_scoped: info_path = join(output_directory, 'scoped', pkg_spec.scope, pkg_spec.package_name) + '.json' tgz_directory = join(output_directory, 'scoped', pkg_spec.scope, 'tgz') else: info_path = join(output_directory, pkg_spec.package_name) + '.json' tgz_directory = join(output_directory, 'tgz') tgz_path = join(tgz_directory, basename(tarball_url)) try: if not exists(tgz_directory): makedirs(tgz_directory) except oserror: logger.exception("Error creating output directory.") with open(info_path, 'w') as f: dump(info, f, indent=4) # file_shasum = get_file_hash(tgz_path) if exists(tgz_path) else None # if the tarball exists and has a valid checksum, don't download a new copy. # if (exists(tgz_path) and not force) and (file_shasum == expected_shasum): if exists(tgz_path) and not force: logger.info( 'Locally cached package: %s version: %s (latest: %s) at: %s', pkg_spec.registry_package_name, download_version, latest, tarball_url) else: logger.info('Getting package: %s version: %s (latest: %s) at: %s', pkg_spec.registry_package_name, download_version, latest, tarball_url) tarball = get(tarball_url) with open(tgz_path, 'wb') as f: f.write(tarball.content) file_shasum = get_file_hash(tgz_path) if exists(tgz_path) else None if not file_shasum == expected_shasum: logger.warning( '''Package %s from %s downloaded by hash: %s doesn't match expected hash: %s''', pkg_spec.registry_package_name, tarball_url, file_shasum, expected_shasum) # to prevent circular dependency problems, track downloaded packages/versions. if pkg_spec.registry_package_name not in duplicate_download_preventer: duplicate_download_preventer[pkg_spec.registry_package_name] = set() duplicate_download_preventer[pkg_spec.registry_package_name].add( download_version) # cache all package dependencies and optoinal dependencies. logger.info('Processing dependencies: %s', pkg_spec.registry_package_name) for dependency, version in version_info.get('dependencies', {}).items(): download_package(output_directory, dependency + '@' + version, duplicate_download_preventer, force) logger.info('Processing optional dependencies: %s', pkg_spec.registry_package_name) for dependency, version in version_info.get('dependencies', {}).items(): download_package(output_directory, dependency + '@' + version, duplicate_download_preventer, force) logger.info('Done with package: %s', pkg_spec.registry_package_name) sleep(NICENESS)
from semver import max_satisfying, make_semver def partition(versions, loose=False): oks, ngs = [], [] for v in versions: try: oks.append(make_semver(v, loose=loose)) except ValueError as e: ngs.append((v, e)) return oks, ngs versions = ["1.a.1", "master", "X.2", "1.2.1", "1.3", "2.1"] range_ = '1.3' loose = True oks, ngs = partition(versions, loose=loose) print(oks) # [<SemVer 1.2.1 >, <SemVer 1.3.0 >, <SemVer 2.1.0 >] print(max_satisfying(oks, range_, loose=loose)) # 1.3.0 # print(make_semver("X.2", loose=True))