diff --git a/README.md b/README.md index 3c0aa9e..9f3bc8e 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +*This repo has had small changes to now work with python3.* + # rhsecapi `rhsecapi` makes it easy to interface with the [Red Hat Security Data API](https://access.redhat.com/documentation/en/red-hat-security-data-api/) -- even from [behind a proxy](https://github.com/ryran/rhsecapi/issues/29). From the rpm description: diff --git a/rhsda.py b/rhsda.py index 60c0ea8..a775db9 100644 --- a/rhsda.py +++ b/rhsda.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python3 # -*- coding: utf-8 -*- #------------------------------------------------------------------------------- # Copyright 2016, 2017 @@ -16,7 +16,7 @@ #------------------------------------------------------------------------------- # Modules from standard library -from __future__ import print_function +# from __future__ import print_function import requests import logging import sys @@ -24,10 +24,12 @@ import textwrap, fcntl, termios, struct import json import signal -import copy_reg +import copyreg import types +import multiprocessing as mp import multiprocessing.dummy as multiprocessing from argparse import Namespace +from io import TextIOWrapper # Logging @@ -101,7 +103,6 @@ # A list of all fields + all aliases cveFields.all_plus_aliases = list(cveFields.all) cveFields.all_plus_aliases.extend([k for k in cveFields.aliases]) -del(k) # Regex to match a CVE id string @@ -118,14 +119,14 @@ def _reduce_method(m): else: return getattr, (m.__self__, m.__func__.__name__) -copy_reg.pickle(types.MethodType, _reduce_method) +copyreg.pickle(types.MethodType, _reduce_method) # Set default number of worker threads -if multiprocessing.cpu_count() <= 2: +if mp.cpu_count() <= 2: numThreadsDefault = 4 else: - numThreadsDefault = multiprocessing.cpu_count() * 2 + numThreadsDefault = mp.cpu_count() * 2 def jprint(jsoninput): @@ -175,7 +176,7 @@ class ApiClient: def __init__(self, logLevel='notice'): self.cfg = Namespace() - self.cfg.apiUrl = 'https://access.redhat.com/labs/securitydataapi' + self.cfg.apiUrl = 'https://access.redhat.com/hydra/rest/securitydata' logger.setLevel(logLevel.upper()) def _get_terminal_width(self): @@ -192,7 +193,9 @@ def __validate_out_format(self, oF): if oF not in outFormats: raise ValueError("Invalid outFormat type ('{0}') requested; should be one of: {1}".format(oF, ", ".join(outFormats))) - def __get(self, url, params={}): + def __get(self, url, params=None): + if params is None: + params = {} url = self.cfg.apiUrl + url u = "" if params: @@ -217,7 +220,7 @@ def __get(self, url, params={}): if 'application/json' in r.headers['Content-Type']: return r.json() else: - return r.content + return r.text def _find(self, dataType, params, outFormat): self.__validate_data_type(dataType) @@ -368,11 +371,11 @@ def __stripjoin(self, input, oneLineEach=False): text = "" if isinstance(input, list): if oneLineEach: - text = "\n".join(input).encode('utf-8').strip() + text = "\n".join(input).strip() else: - text = " ".join(input).encode('utf-8').strip() + text = " ".join(input).strip() else: - text = input.encode('utf-8').strip() + text = input.strip() if oneLineEach: text = "\n" + text text = re.sub(r"\n[\n\s]*", "\n ", text) @@ -728,7 +731,7 @@ def mget_cves(self, cves, numThreads=0, onlyCount=False, outFormat='plaintext', """ if outFormat not in ['plaintext', 'json', 'jsonpretty']: raise ValueError("Invalid outFormat ('{0}') requested; should be one of: 'plaintext', 'json', 'jsonpretty'".format(outFormat)) - if isinstance(cves, str) or isinstance(cves, file): + if isinstance(cves, str) or isinstance(cves, TextIOWrapper): cves = extract_cves_from_input(cves) elif not isinstance(cves, list): raise ValueError("Invalid 'cves=' argument input; must be list, string, or file obj") diff --git a/rhsecapi.py b/rhsecapi.py index 03a463b..b55a7ea 100755 --- a/rhsecapi.py +++ b/rhsecapi.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python3 # -*- coding: utf-8 -*- # PYTHON_ARGCOMPLETE_OK #------------------------------------------------------------------------------- @@ -83,7 +83,7 @@ def fpaste_it(inputdata, lang='text', author=None, password=None, private='no', params['paste_user'] = author # Check size of what we're about to post and raise exception if too big # FIXME: Figure out how to do this in requests without wasteful call to urllib.urlencode() - from urllib import urlencode + from urllib.parse import urlencode p = urlencode(params) pasteSizeKiB = len(p)/1024.0 if pasteSizeKiB >= 512: @@ -98,11 +98,11 @@ def fpaste_it(inputdata, lang='text', author=None, password=None, private='no', # If no json returned, we've hit some weird error from tempfile import NamedTemporaryFile tmp = NamedTemporaryFile(delete=False) - print(r.content, file=tmp) + tmp.write(r.content) tmp.flush() raise ValueError("Fedora Pastebin client ERROR: Didn't receive expected JSON response (saved to '{0}' for debugging)".format(tmp.name)) # Error keys adapted from Jason Farrell's fpaste - if j.has_key('error'): + if 'error' in j: err = j['error'] if err == 'err_spamguard_php': raise ValueError("Fedora Pastebin server ERROR: Poster's IP rejected as malicious") @@ -118,7 +118,7 @@ def fpaste_it(inputdata, lang='text', author=None, password=None, private='no', raise ValueError("Fedora Pastebin server ERROR: '{0}'".format(err)) # Put together URL with optional hash if requested pasteUrl = '{0}/{1}'.format(url, j['result']['id']) - if 'yes' in private and j['result'].has_key('hash'): + if 'yes' in private and 'hash' in j['result']: pasteUrl += '/{0}'.format(j['result']['hash']) return pasteUrl @@ -283,7 +283,8 @@ def parse_args(): if o.showHelp: from tempfile import NamedTemporaryFile from subprocess import call - tmp = NamedTemporaryFile(prefix='{0}-help-'.format(prog), suffix='.txt') + #tmp = NamedTemporaryFile(prefix='{0}-help-'.format(prog), suffix='.txt') + tmp = NamedTemporaryFile(mode='w+', prefix='{0}-help-'.format(prog), suffix='.txt') p.print_help(file=tmp) tmp.flush() call(['less', tmp.name]) @@ -347,7 +348,7 @@ def parse_args(): def main(opts): apiclient = rhsda.ApiClient(opts.loglevel) from os import environ - if environ.has_key('RHSDA_URL') and environ['RHSDA_URL'].startswith('http'): + if 'RHSDA_URL' in environ and environ['RHSDA_URL'].startswith('http'): apiclient.cfg.apiUrl = environ['RHSDA_URL'] searchOutput = "" iavaOutput = "" @@ -404,7 +405,7 @@ def main(opts): except ValueError as e: print(e, file=sys.stderr) logger.error("Submitting to pastebin failed; print results to stdout instead? [y]") - answer = raw_input("> ") + answer = input("> ") if "y" in answer or len(answer) == 0: print(data, end="") else: