def get(pattern, cache_filename, no_cache, update_cache): cache_path = Path(cache_filename) data = {} fetch_from_scc = False if cache_path.exists() and cache_path.is_file() and not no_cache: data = cache.load(cache_filename) try: data["product"] except KeyError as err: fetch_from_scc = True else: fetch_from_scc = True if fetch_from_scc: response = request.fetch("https://scc.suse.com/api/package_search/products") try: data["product"] = response["data"] except KeyError as err: print_err(f"Error: No data key found in scc api response, {err}") sys.exit(1) ret = [] if not pattern: ret = data["product"] else: for product in data["product"]: for k in product.keys(): if pattern in str(product[k]): ret.append(product) break if update_cache: cache.save("product", cache_filename, ret) return ret
def load(filename) -> Dict: """Load cache from filename The cache file is a json file and retruns a dictionary Parameters ---------- filename: str path to the json file Returns ------- dict the content of the cache json file as dict Raises ------ SystemExit If PermissionError raises if unable to decode cache file as JSON """ data = {} try: with open(filename, "r") as f: data = json.load(f) except PermissionError as err: print_err({err}) sys.exit(13) except json.decoder.JSONDecodeError as err: print_err(f"unable to parse {filename} as JSON, {err}") sys.exit(1) except FileNotFoundError: pass return data
def lookup_product(identifier, filename): """Lookup a product in the cache Lookup a product in cache based on identifier and return the id Parameters ---------- identifier: str product identifier to search for filename: str path to cache file to use Returns ------- int the product id Raises ------ SystemExit if no product matches the identifier """ data = load(filename) for product in data["product"]: if product["identifier"] == identifier: return product["id"] print_err(f"Couldn't find id for '{identifier}'") sys.exit(1)
def get(product, pattern, cache_filename): """Get the packages information from SUSE Customer Center for a particular product Parameters ---------- product: str product id or identifier pattern: str search pattern in package name, if blank all packages will be returned cache_filename: str path to the cache file name to be used to lookup product id if product identifier is specified instead of product it Returns ------- list a list of packages that matches the search result each package is a dict Raises ------ SystemExit if the response from SUSE Customer Center is not parsable """ try: product_id = int(product) except ValueError: product_id = cache.lookup_product(product, cache_filename) url = f"https://scc.suse.com/api/package_search/packages?product_id={product_id}&query={pattern}" response = request.fetch(url) try: response["data"] except KeyError as err: print_err(f"Error: No data key found, {err}") sys.exit(1) return response["data"]
def save(key, filename, data): """Saves cache to filename The cache is a json file and is loaded as dictionary. Parameters --------- key: str the dictionary key that the data should be accociated with. filename: str the path to filename to save the cache to data: list data to be saved in cache Returns ------- None Raises ------ SystemExit if key is not known if data is not of type list if PermissionError is raised when saving to filename """ cache_data = {} if not key in ["product", "patchproducts"]: print_err(f"trying to save cache with unknown key '{key}'") sys.exit(1) if not isinstance(data, list): print_err("Must have list as data source to save cache") sys.exit(1) if Path(filename).is_file(): cache_data = load(filename) cache_data[key] = data try: cache_data["age"][key] = datetime.now().strftime("%Y-%m-%d") except KeyError: cache_data["age"] = {} cache_data["age"][key] = datetime.now().strftime("%Y-%m-%d") try: with open(filename, "w") as f: json.dump(cache_data, f) except PermissionError as err: print_err({err}) sys.exit(13)
def fetch(url, type="json"): try: response = requests.get(url, headers) except requests.exceptions.RequestException as err: print_err({err}) sys.exit(1) if response.status_code != 200: print_err(f"Got status code '{response.status_code}' from {url}", ) sys.exit(1) if type == "json": try: return response.json() except json.decoder.JSONDecodeError as err: print_err(f"Couldn't parse json response {err}") sys.exit(1) elif type == "html": return response.text
def get(pattern, cachefile, no_cache, update_cache) -> List: """Fetch the products used by SCC Patches Parameters ---------- pattern: str Only return products where 'pattern' matches product name cachefile: str Patch to cache file no_cache: bool If true the cache file will not be used update_cache: bool if true the cache file will be updated Returns: List A list of Dict representing the products Raises: SystemExit if response from SUSE Customer Center is not parsable """ data = {} if not update_cache and ( Path(cachefile).exists() and Path(cachefile).is_file() and not no_cache ): data = cache.load(cachefile) try: data["patchproducts"] except KeyError as err: print_err("no patch product key found in cachefile") sys.exit(1) else: response = request.fetch("https://scc.suse.com/patches", "html") matches = re.findall(".*productsData=(.*)[^ \t\n\r\f\v]+.*", response) if len(matches) != 1: if len(matches) > 1: print_err("to many matches in response from SCC") else: print_err("no matches in response from SCC") exit(1) try: data["patchproducts"] = json.loads(matches[0]) except json.decoder.JSONDecodeError as err: print_err(f"Couldn't parse json response {err}") sys.exit(1) ret = [] if not pattern: ret = data["patchproducts"] else: for product in data["patchproducts"]: for k in product.keys(): if pattern in str(product[k]): ret.append(product) break if update_cache: cache.save("patchproducts", cachefile, data["patchproducts"]) return ret
def get(cachefile, shell=None): """Generates a shell completion script Parameters ---------- cachefile: str Path to cache file shell: str, optional Name of the shell to generate completion script for if not provided the function will try to use the SHELL environment varable Raises ------ SystemExit if an unknown shell is provided if shell is not provided and SHELL environ is not set if completion generation file is not readable """ if shell == None: try: os.environ["SHELL"] except KeyError: print_err( "Couldn't determin shell, you need to specify shell or set $SHELL environment variable", ) sys.exit(1) shell = os.path.basename(os.environ.get("SHELL")) if shell == "bash": filename = f"{os.path.dirname(os.path.realpath(__file__))}/completion.sh" else: print_err(f"Unsupported shell specified '{shell}'") sys.exit(1) try: with open(filename, "r") as f: # pragma: no cover fc = f.read() except FileNotFoundError as err: print_err({err}) sys.exit(2) data = cache.load(cachefile) sps_patch_product_complete = "" sps_patch_version_complete = "" sps_patch_arch_complete = "" try: sps_patch_product_complete = " ".join( patchproducts.short(data["patchproducts"], "name")) sps_patch_version_complete = " ".join( patchproducts.short(data["patchproducts"], "version")) sps_patch_arch_complete = " ".join( patchproducts.short(data["patchproducts"], "architecture")) except KeyError: print_warn( "Patch product information not found in cache file\nTo enable completion for patch products run: sps patchproduct --update-cache" ) products = [] try: for product in data["product"]: products.append(product["identifier"]) except KeyError: print_warn( "Product information not found in cache file\nTo enable completion for products run: sps product --update-cache" ) sps_package_product_complete = " ".join(products) fc = fc.replace("{sps_patch_product_complete}", sps_patch_product_complete) fc = fc.replace("{sps_patch_version_complete}", sps_patch_version_complete) fc = fc.replace("{sps_patch_arch_complete}", sps_patch_arch_complete) fc = fc.replace("{sps_package_product_complete}", sps_package_product_complete) return fc