def search(self, tokens, case_sensitive=False, return_type=qp.Query.RETURN_PACKAGES, start_point=None, num_to_return=None, matching_version=None, return_latest=False): """Searches the catalog for actions or packages (as determined by 'return_type') matching the specified 'tokens'. 'tokens' is a string using pkg(7) query syntax. 'case_sensitive' is an optional, boolean value indicating whether matching entries must have the same case as that of the provided tokens. 'return_type' is an optional, constant value indicating the type of results to be returned. This constant value should be one provided by the pkg.server.query_parser.Query class. 'start_point' is an optional, integer value indicating how many search results should be discarded before returning any results. None is interpreted to mean 0. 'num_to_return' is an optional, integer value indicating how many search results should be returned. None means return all results. 'matching_version' is a string in the format expected by the pkg.version.MatchingVersion class that will be used to further filter the search results as they are retrieved. 'return_latest' is an optional, boolean value that will cause only the latest versions of packages to be returned. Ignored if 'return_type' is not qp.Query.RETURN_PACKAGES. """ if not tokens: return [] tokens = tokens.split() if not self.search_available: return [] if start_point is None: start_point = 0 def filter_results(results, mver): found = 0 last_stem = None for result in results: if found and \ ((found - start_point) >= num_to_return): break if result[1] == qp.Query.RETURN_PACKAGES: pfmri = result[2] elif result[1] == qp.Query.RETURN_ACTIONS: pfmri = result[2][0] if mver is not None: if mver != pfmri.version: continue if return_latest and \ result[1] == qp.Query.RETURN_PACKAGES: # Latest version filtering can only be # done for packages as only they are # guaranteed to be in version order. stem = result[2].pkg_name if last_stem == stem: continue else: last_stem = stem found += 1 if found > start_point: yield result def filtered_search(results, mver): try: result = next(results) except StopIteration: return return_type = result[1] results = itertools.chain([result], results) if return_latest and \ return_type == qp.Query.RETURN_PACKAGES: def cmp_fmris(resa, resb): a = resa[2] b = resb[2] if a.pkg_name == b.pkg_name: # Version in descending order. return misc.cmp(a.version, b.version) * -1 return misc.cmp(a, b) return filter_results(sorted(results, key=cmp_to_key(cmp_fmris)), mver) return filter_results(results, mver) if matching_version or return_latest: # Additional filtering needs to be performed and # the results yielded one by one. mver = None if matching_version: mver = version.MatchingVersion(matching_version, None) # Results should be retrieved here so that an exception # can be immediately raised. query = qp.Query(" ".join(tokens), case_sensitive, return_type, None, None) res_list = self._depot.repo.search([str(query)], pub=self._pub) if not res_list: return return filtered_search(res_list[0], mver) query = qp.Query(" ".join(tokens), case_sensitive, return_type, num_to_return, start_point) res_list = self._depot.repo.search([str(query)], pub=self._pub) if not res_list: return return res_list[0]
def extract_matching_fmris(pkgs, patterns=None, matcher=None, constraint=None, counthash=None, versions=None): """Iterate through the given list of PkgFmri objects, looking for packages matching 'pattern' in 'patterns', based on the function in 'matcher' and the versioning constraint described by 'constraint'. If 'matcher' is None, uses fmri subset matching as the default. If 'patterns' is None, 'versions' may be specified, and looks for packages matching the patterns specified in 'versions'. When using 'version', the 'constraint' parameter is ignored. 'versions' should be a list of strings of the format: release,build_release-branch:datetime ...with a value of '*' provided for any component to be ignored. '*' or '?' may be used within each component value and will act as wildcard characters ('*' for one or more characters, '?' for a single character). Returns a sorted list of PkgFmri objects, newest versions first. If 'counthash' is a dictionary, instead store the number of matched fmris for each package that matches.""" if not matcher: matcher = fmri.fmri_match if patterns is None: patterns = [] elif not isinstance(patterns, list): patterns = [patterns] if versions is None: versions = [] elif not isinstance(versions, list): versions = [version.MatchingVersion(versions, None)] else: for i, ver in enumerate(versions): versions[i] = version.MatchingVersion(ver, None) # 'pattern' may be a partially or fully decorated fmri; we want # to extract its name and version to match separately against # the catalog. # XXX "5.11" here needs to be saner tuples = {} for pattern in patterns: if isinstance(pattern, fmri.PkgFmri): tuples[pattern] = pattern.tuple() else: assert pattern != None tuples[pattern] = \ fmri.PkgFmri(pattern, "5.11").tuple() def by_pattern(p): cat_pub, cat_name, cat_version = p.tuple() for pattern in patterns: pat_pub, pat_name, pat_version = tuples[pattern] if (fmri.is_same_publisher(pat_pub, cat_pub) or not \ pat_pub) and matcher(cat_name, pat_name): if not pat_version or \ p.version.is_successor( pat_version, constraint) or \ p.version == pat_version: if counthash is not None: if pattern in counthash: counthash[pattern] += 1 else: counthash[pattern] = 1 if pat_pub: p.set_publisher(pat_pub) return p def by_version(p): for ver in versions: if ver == p.version: if counthash is not None: sver = str(ver) if sver in counthash: counthash[sver] += 1 else: counthash[sver] = 1 return p ret = [] if patterns: for p in pkgs: res = by_pattern(p) if res is not None: ret.append(res) elif versions: for p in pkgs: res = by_version(p) if res is not None: ret.append(res) return sorted(ret, reverse=True)