Hello community, here is the log from the commit of package osc for openSUSE:Factory checked in at 2020-07-09 13:20:49 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/osc (Old) and /work/SRC/openSUSE:Factory/.osc.new.3060 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "osc" Thu Jul 9 13:20:49 2020 rev:150 rq:819698 version:0.170.0 Changes: -------- --- /work/SRC/openSUSE:Factory/osc/osc.changes 2020-06-02 14:35:01.922836714 +0200 +++ /work/SRC/openSUSE:Factory/.osc.new.3060/osc.changes 2020-07-09 13:22:06.165788295 +0200 @@ -1,0 +2,28 @@ +Thu Jul 9 08:06:35 UTC 2020 - Marco Strigl <marco.str...@suse.com> + +- 0.170.0 + * fix code for python3.8 and python3.9 + * remove dead code + * fix tests + * don't use chardet to guess encoding. utf-8 or latin-1 is now assumed + This will speed up decoding (bsc#1173926) + * escape sequences are interpreted correctly on -m + osc sr -m "1st line\n2nd line" + results in + 1st line + 2nd line + * add status filter to osc results + * add --brief to osc prjresults + Gives: + build openSUSE_Leap_15.1 x86_64 succeeded + build openSUSE_Leap_15.0 x86_64 succeeded + build openSUSE_Leap_15.0 armv7l unresolvable + build openSUSE_Factory_ARM armv7l succeeded + build openSUSE_Factory_ARM aarch64 succeeded + * osc prjresults -s <status>: status can now be the long text like "succeeded", "failed" + * improve error message if osc service <servicename> is not run in working directory + * web links are now also printed for osc mr when print_web_links=1 is set +- Spec: + * re-enable test suite + +------------------------------------------------------------------- Old: ---- osc-0.169.1.tar.gz New: ---- osc-0.170.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ osc.spec ++++++ --- /var/tmp/diff_new_pack.Y2jfES/_old 2020-07-09 13:22:06.769790204 +0200 +++ /var/tmp/diff_new_pack.Y2jfES/_new 2020-07-09 13:22:06.773790217 +0200 @@ -27,12 +27,12 @@ %define use_python python %endif -%define version_unconverted 0.169.1 +%define version_unconverted 0.170.0 %define osc_plugin_dir %{_prefix}/lib/osc-plugins %define macros_file macros.osc Name: osc -Version: 0.169.1 +Version: 0.170.0 Release: 0 Summary: Open Build Service Commander License: GPL-2.0-or-later @@ -181,7 +181,7 @@ %if 0%{?suse_version} >= 1500 %check cd tests -#%%{use_python} suite.py +%{use_python} suite.py %endif %clean ++++++ PKGBUILD ++++++ --- /var/tmp/diff_new_pack.Y2jfES/_old 2020-07-09 13:22:06.801790305 +0200 +++ /var/tmp/diff_new_pack.Y2jfES/_new 2020-07-09 13:22:06.805790318 +0200 @@ -1,5 +1,5 @@ pkgname=osc -pkgver=0.169.1 +pkgver=0.170.0 pkgrel=0 pkgdesc="Open Build Service client" arch=('x86_64') ++++++ _service ++++++ --- /var/tmp/diff_new_pack.Y2jfES/_old 2020-07-09 13:22:06.821790369 +0200 +++ /var/tmp/diff_new_pack.Y2jfES/_new 2020-07-09 13:22:06.821790369 +0200 @@ -1,7 +1,7 @@ <services> <service name="tar_scm" mode="disabled"> - <param name="version">0.169.1</param> - <param name="revision">0.169.1</param> + <param name="version">0.170.0</param> + <param name="revision">0.170.0</param> <param name="url">git://github.com/openSUSE/osc.git</param> <param name="scm">git</param> </service> ++++++ debian.changelog ++++++ --- /var/tmp/diff_new_pack.Y2jfES/_old 2020-07-09 13:22:06.873790533 +0200 +++ /var/tmp/diff_new_pack.Y2jfES/_new 2020-07-09 13:22:06.877790546 +0200 @@ -1,4 +1,4 @@ -osc (0.169.1) unstable; urgency=low +osc (0.170.0) unstable; urgency=low - Package for Python3 -- Nick Brown <nick.br...@att.com> Wed, 30 Jan 2020 14:49:30 +0000 ++++++ osc-0.169.1.tar.gz -> osc-0.170.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.169.1/.travis.yml new/osc-0.170.0/.travis.yml --- old/osc-0.169.1/.travis.yml 2020-05-29 20:35:00.000000000 +0200 +++ new/osc-0.170.0/.travis.yml 2020-07-09 09:57:53.000000000 +0200 @@ -3,6 +3,7 @@ - '2.7' - '3.6' - '3.7' + - '3.8' addons: apt: packages: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.169.1/NEWS new/osc-0.170.0/NEWS --- old/osc-0.169.1/NEWS 2020-05-29 20:35:00.000000000 +0200 +++ new/osc-0.170.0/NEWS 2020-07-09 09:57:53.000000000 +0200 @@ -1,3 +1,26 @@ +0.170 + - fix code for python3.8 and python3.9 + - remove dead code + - fix tests + - don't use chardet to guess encoding. utf-8 or latin-1 is now assumed + This will speed up decoding (bsc#1173926) + - escape sequences are interpreted correctly on -m + osc sr -m "1st line\n2nd line" + results in + 1st line + 2nd line + - add status filter to osc results + - add --brief to osc prjresults + Gives: + build openSUSE_Leap_15.1 x86_64 succeeded + build openSUSE_Leap_15.0 x86_64 succeeded + build openSUSE_Leap_15.0 armv7l unresolvable + build openSUSE_Factory_ARM armv7l succeeded + build openSUSE_Factory_ARM aarch64 succeeded + - osc prjresults -s <status>: status can now be the long text like "succeeded", "failed" + - improve error message if osc service <servicename> is not run in working directory + - web links are now also printed for osc mr when print_web_links=1 is set + 0.169.1 - add --ccache option to osc getbinaries diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.169.1/README new/osc-0.170.0/README --- old/osc-0.169.1/README 2020-05-29 20:35:00.000000000 +0200 +++ new/osc-0.170.0/README 2020-07-09 09:57:53.000000000 +0200 @@ -24,10 +24,6 @@ (which is easier if you develop on osc). -The program needs the cElementTree python module installed. On SUSE, the -respective package is called python-elementtree (before 10.2: python-xml). - - CONFIGURATION: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.169.1/osc/build.py new/osc-0.170.0/osc/build.py --- old/osc-0.169.1/osc/build.py 2020-05-29 20:35:00.000000000 +0200 +++ new/osc-0.170.0/osc/build.py 2020-07-09 09:57:53.000000000 +0200 @@ -28,9 +28,12 @@ from . import oscerr import subprocess try: + # Works up to Python 3.8, needed for Python < 3.3 (inc 2.7) from xml.etree import cElementTree as ET except ImportError: - import cElementTree as ET + # will import a fast implementation from 3.3 onwards, needed + # for 3.9+ + from xml.etree import ElementTree as ET from .conf import config, cookiejar diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.169.1/osc/commandline.py new/osc-0.170.0/osc/commandline.py --- old/osc-0.169.1/osc/commandline.py 2020-05-29 20:35:00.000000000 +0200 +++ new/osc-0.170.0/osc/commandline.py 2020-07-09 09:57:53.000000000 +0200 @@ -911,6 +911,10 @@ # If project or package arguments missing, assume to work # with project and/or package in current local directory. attributepath = [] + + if opts.message: + opts.message = str(opts.message.encode().decode('unicode_escape')) + if cmd in ['prj', 'prjconf']: if len(args) < 1: apiurl = store_read_apiurl(os.curdir) @@ -1212,6 +1216,9 @@ elif opts.no_update: src_update = "noupdate" + if opts.message: + opts.message = str(opts.message.encode().decode('unicode_escape')) + myreqs = [] if opts.supersede: myreqs = [opts.supersede] @@ -1872,6 +1879,8 @@ if not opts.message: opts.message = edit_message() + else: + opts.message = str(opts.message.encode().decode('unicode_escape')) xml = """<request> %s <state name="new"/> <description>%s</description> </request> """ % \ (actionsxml, _html_escape(opts.message or "")) @@ -1949,6 +1958,8 @@ raise oscerr.WrongOptions('invalid \'--role\': either specify \'maintainer\' or \'bugowner\'') if not opts.message: opts.message = edit_message() + else: + opts.message = str(opts.message.encode().decode('unicode_escape')) r = Request() if user.startswith('group:'): @@ -2027,6 +2038,8 @@ footer = textwrap.TextWrapper(width = 66).fill( 'please explain why you like to delete project %s' % project) opts.message = edit_message(footer) + else: + opts.message = str(opts.message.encode().decode('unicode_escape')) r = Request() r.add_action('delete', tgt_project=project, tgt_package=package, tgt_repository=repository) @@ -2074,6 +2087,8 @@ 'please explain why you like to change the devel project of %s/%s to %s/%s' % (project, package, devel_project, devel_package)) opts.message = edit_message(footer) + else: + opts.message = str(opts.message.encode().decode('unicode_escape')) r = Request() r.add_action('change_devel', src_project=devel_project, src_package=devel_package, @@ -2241,6 +2256,9 @@ if opts.incoming: conf.config['include_request_from_project'] = False + if opts.message: + opts.message = str(opts.message.encode().decode('unicode_escape')) + if args[0] == 'help': return self.do_help(['help', 'request']) @@ -2785,7 +2803,8 @@ raise oscerr.WrongArgs('Too many arguments (required none or two)') else: raise oscerr.WrongArgs('Too few arguments (required none or two)') - + if opts.message: + opts.message = str(opts.message.encode().decode('unicode_escape')) try: copy_pac(apiurl, project, package, apiurl, project, package, expand=True, comment=opts.message) except HTTPError as e: @@ -2990,7 +3009,7 @@ rev, dummy = parseRevisionOption(opts.revision) if opts.message: - comment = opts.message + comment = str(opts.message.encode().decode('unicode_escape')) else: if not rev: rev = show_upstream_rev(src_apiurl, src_project, src_package) @@ -3123,6 +3142,8 @@ if not opts.message: opts.message = edit_message() + else: + opts.message = str(opts.message.encode().decode('unicode_escape')) r = create_release_request(apiurl, source_project, opts.message) print(r.reqid) @@ -3156,6 +3177,8 @@ maintenance_attribute = conf.config['maintenance_attribute'] if opts.attribute: maintenance_attribute = opts.attribute + if opts.message: + opts.message = str(opts.message.encode().decode('unicode_escape')) source_project = target_project = None @@ -3292,6 +3315,8 @@ if not opts.message: opts.message = edit_message() + else: + opts.message = str(opts.message.encode().decode('unicode_escape')) supersede_existing = False reqs = [] @@ -3304,6 +3329,13 @@ r = create_maintenance_request(apiurl, source_project, source_packages, target_project, release_project, opt_sourceupdate, opts.message, opts.enforce_branching) print(r.reqid) + if conf.config['print_web_links']: + root = ET.fromstring(b''.join(show_configuration(apiurl))) + node = root.find('obs_url') + if node is None or not node.text: + raise oscerr.APIError('obs_url configuration element expected') + obs_url = node.text + print('%s/request/show/%s' % (obs_url, r.reqid)) if supersede_existing: for req in reqs: @@ -3508,6 +3540,9 @@ if len(args) >= 4: tpackage = args[3] + if opts.message: + opts.message = str(opts.message.encode().decode('unicode_escape')) + try: exists, targetprj, targetpkg, srcprj, srcpkg = \ branch_pkg(apiurl, args[0], args[1], @@ -3601,7 +3636,7 @@ msg = '' if opts.message: - msg = opts.message + msg = str(opts.message.encode().decode('unicode_escape')) else: msg = edit_message() @@ -3648,7 +3683,7 @@ msg = '' if opts.message: - msg = opts.message + msg = str(opts.message.encode().decode('unicode_escape')) else: msg = edit_message() @@ -3694,6 +3729,8 @@ ${cmd_option_list} """ apiurl = self.get_api_url() + if opts.message: + opts.message = str(opts.message.encode().decode('unicode_escape')) kind = 'prj' path_args = (project,) if package is not None: @@ -3734,7 +3771,7 @@ msg = '' if opts.message: - msg = opts.message + msg = str(opts.message.encode().decode('unicode_escape')) else: msg = edit_message() @@ -3932,6 +3969,8 @@ help='do not fail if the source or target project/package does not exist on the server') @cmdln.option('-u', '--unexpand', action='store_true', help='diff unexpanded version if sources are linked') + @cmdln.option('--xml', action='store_true', + help='show diff as xml (only for issues diff)') def do_rdiff(self, subcmd, opts, *args): """${cmd_name}: Server-side "pretty" diff of two packages @@ -4009,9 +4048,10 @@ new_project, new_package, rev2, not opts.plain, opts.missingok, meta=opts.meta, expand=not opts.unexpand, - onlyissues=opts.issues_only) + onlyissues=opts.issues_only, + xml=opts.xml) if opts.issues_only: - print(rdiff) + print(decode_it(rdiff)) else: run_pager(rdiff) @@ -4807,7 +4847,7 @@ msg = '' if opts.message: - msg = opts.message + msg = str(opts.message.encode().decode('unicode_escape')) elif opts.file: if opts.file == '-': msg = sys.stdin.read() @@ -5256,6 +5296,8 @@ help='Show results only for specified repo(s)') @cmdln.option('-a', '--arch', action='append', default = [], help='Show results only for specified architecture(s)') + @cmdln.option('-b', '--brief', action='store_true', + help='show the result in "pkgname repo arch result". Default for -f') @cmdln.option('-v', '--verbose', action='store_true', default=False, help='more verbose output') @cmdln.option('--no-multibuild', action='store_true', default=False, @@ -5266,6 +5308,10 @@ help='list packages vertically instead horizontally for entire project') @cmdln.option('-w', '--watch', action='store_true', help='watch the results until all finished building') + @cmdln.option('-s', '--status-filter', + help='only show packages with the given build status') + @cmdln.option('-f', '--failed', action='store_true', + help='show only failed results') @cmdln.option('', '--xml', action='store_true', default=False, help='generate output in XML (former results_meta)') @cmdln.option('', '--csv', action='store_true', default=False, @@ -5304,10 +5350,16 @@ if project == None: raise oscerr.WrongOptions("No project given") + if opts.failed and opts.status_filter: + raise oscerr.WrongArgs('-s and -f cannot be used together') + + if opts.failed: + opts.status_filter = 'failed' + opts.brief = True + if package == None: opts.hide_legend = None opts.name_filter = None - opts.status_filter = None opts.show_non_building = None opts.show_excluded = None return self.do_prjresults('prjresults', opts, *args) @@ -5317,7 +5369,8 @@ kwargs = {'apiurl': apiurl, 'project': project, 'package': package, 'lastbuild': opts.last_build, 'repository': opts.repo, - 'arch': opts.arch, 'wait': opts.watch, 'showexcl': opts.show_excluded} + 'arch': opts.arch, 'wait': opts.watch, 'showexcl': opts.show_excluded, + 'code': opts.status_filter} if opts.multibuild_package: opts.no_multibuild = False kwargs['multibuild_packages'] = opts.multibuild_package @@ -5345,6 +5398,8 @@ # as well when adding a new option! @cmdln.option('-q', '--hide-legend', action='store_true', help='hide the legend') + @cmdln.option('-b', '--brief', action='store_true', + help='show the result in "pkgname repo arch result"') @cmdln.option('-w', '--watch', action='store_true', help='watch the results until all finished building, only supported with --xml') @cmdln.option('-c', '--csv', action='store_true', @@ -5403,7 +5458,7 @@ csv=opts.csv, status_filter=opts.status_filter, \ name_filter=opts.name_filter, repo=opts.repo, \ arch=opts.arch, vertical=opts.vertical, \ - show_excluded=opts.show_excluded))) + show_excluded=opts.show_excluded, brief=opts.brief))) @cmdln.option('-q', '--hide-legend', action='store_true', help='hide the legend') @@ -6908,18 +6963,24 @@ args = slash_split(args) project = package = singleservice = mode = None apiurl = self.get_api_url() + remote_commands = ('remoterun', 'rr', 'merge', 'wait') if len(args) < 1: raise oscerr.WrongArgs('No command given.') elif len(args) < 3: - if is_package_dir(os.curdir): - project = store_read_project(os.curdir) + if args[0] in remote_commands: + if not is_package_dir(os.curdir): + msg = ('Either specify the project and package or execute ' + 'the command in a package working copy.') + raise oscerr.WrongArgs(msg) package = store_read_package(os.curdir) + project = store_read_project(os.curdir) else: - raise oscerr.WrongArgs('Too few arguments.') + # raise an appropriate exception if os.curdir is no package wc + store_read_package(os.curdir) if len(args) == 2: singleservice = args[1] - elif len(args) == 3 and args[0] in ('remoterun', 'rr', 'merge', 'wait'): + elif len(args) == 3 and args[0] in remote_commands: project = args[1] package = args[2] else: @@ -8237,7 +8298,7 @@ if requestactionsxml != "": if opts.message: - message = opts.message + message = str(opts.message.encode().decode('unicode_escape')) else: message = edit_message() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.169.1/osc/core.py new/osc-0.170.0/osc/core.py --- old/osc-0.169.1/osc/core.py 2020-05-29 20:35:00.000000000 +0200 +++ new/osc-0.170.0/osc/core.py 2020-07-09 09:57:53.000000000 +0200 @@ -5,7 +5,7 @@ from __future__ import print_function -__version__ = '0.169.1' +__version__ = '0.170.0' # __store_version__ is to be incremented when the format of the working copy # "store" changes in an incompatible way. Please add any needed migration @@ -47,11 +47,13 @@ from cStringIO import StringIO from httplib import IncompleteRead - try: + # Works up to Python 3.8, needed for Python < 3.3 (inc 2.7) from xml.etree import cElementTree as ET except ImportError: - import cElementTree as ET + # will import a fast implementation from 3.3 onwards, needed + # for 3.9+ + from xml.etree import ElementTree as ET from . import oscerr from . import conf @@ -816,7 +818,7 @@ def read_packages(self): """ - Returns an ``xml.etree.cElementTree`` object representing the + Returns an ``xml.etree.ElementTree`` object representing the parsed contents of the project's ``.osc/_packages`` XML file. """ global store @@ -4820,7 +4822,7 @@ def server_diff(apiurl, old_project, old_package, old_revision, new_project, new_package, new_revision, - unified=False, missingok=False, meta=False, expand=True, onlyissues=False, full=True): + unified=False, missingok=False, meta=False, expand=True, onlyissues=False, full=True, xml=False): query = {'cmd': 'diff'} if expand: query['expand'] = 1 @@ -4848,24 +4850,34 @@ u = makeurl(apiurl, ['source', new_project, new_package], query=query) f = http_POST(u) - if onlyissues: - issue_list = [] + if onlyissues and not xml: + del_issue_list = [] + add_issue_list = [] + chn_issue_list = [] root = ET.fromstring(f.read()) node = root.find('issues') for issuenode in node.findall('issue'): - issue_list.append(issuenode.get('label')) - return '\n'.join(issue_list) + if issuenode.get('state') == 'deleted': + del_issue_list.append(issuenode.get('label')) + elif issuenode.get('state') == 'added': + add_issue_list.append(issuenode.get('label')) + else: + chn_issue_list.append(issuenode.get('label')) + string = 'added:\n----------\n' + '\n'.join(add_issue_list) + \ + '\n\nchanged:\n----------\n' + '\n'.join(chn_issue_list) + \ + '\n\ndeleted:\n----------\n' + '\n'.join(del_issue_list) + return string return f.read() def server_diff_noex(apiurl, old_project, old_package, old_revision, new_project, new_package, new_revision, - unified=False, missingok=False, meta=False, expand=True, onlyissues=False): + unified=False, missingok=False, meta=False, expand=True, onlyissues=False, xml=False): try: return server_diff(apiurl, old_project, old_package, old_revision, new_project, new_package, new_revision, - unified, missingok, meta, expand, onlyissues) + unified, missingok, meta, expand, onlyissues, True, xml) except HTTPError as e: msg = None body = None @@ -5683,7 +5695,7 @@ return r -def show_results_meta(apiurl, prj, package=None, lastbuild=None, repository=[], arch=[], oldstate=None, multibuild=False, locallink=False): +def show_results_meta(apiurl, prj, package=None, lastbuild=None, repository=[], arch=[], oldstate=None, multibuild=False, locallink=False, code=None): query = [] if package: query.append('package=%s' % quote_plus(package)) @@ -5695,6 +5707,8 @@ query.append('multibuild=1') if locallink: query.append('locallink=1') + if code: + query.append('code=%s' % quote_plus(code)) for repo in repository: query.append('repository=%s' % quote_plus(repo)) for a in arch: @@ -5767,6 +5781,7 @@ printed = False multibuild_packages = kwargs.pop('multibuild_packages', []) show_excluded = kwargs.pop('showexcl', False) + code_filter = kwargs.get('code') for results in get_package_results(apiurl, project, package, **kwargs): r = [] for res, is_multi in result_xml_to_dicts(results): @@ -5799,11 +5814,14 @@ res['status'] += '(unpublished)' else: res['status'] += '*' - - if is_multi: - r.append(result_line_mb_templ % res) - else: - r.append(result_line_templ % res) + # we need to do the code filtering again, because result_xml_to_dicts returns the code + # of the repository if the result is already prefiltered by the backend. So we need + # to filter out the repository states. + if code_filter is None or code_filter == res['code']: + if is_multi: + r.append(result_line_mb_templ % res) + else: + r.append(result_line_templ % res) if printJoin: if printed: @@ -5856,7 +5874,7 @@ yield xml -def get_prj_results(apiurl, prj, hide_legend=False, csv=False, status_filter=None, name_filter=None, arch=None, repo=None, vertical=None, show_excluded=None): +def get_prj_results(apiurl, prj, hide_legend=False, csv=False, status_filter=None, name_filter=None, arch=None, repo=None, vertical=None, show_excluded=None, brief=False): #print '----------------------------------------' global buildstatus_symbols @@ -5896,8 +5914,8 @@ targets.sort() # filter option + filters = [] if status_filter or name_filter or not show_excluded: - pacs_to_show = [] targets_to_show = [] @@ -5907,20 +5925,20 @@ # a list is needed because if status_filter == "U" # we have to filter either an "expansion error" (obsolete) # or an "unresolvable" state - filters = [] for txt, sym in buildstatus_symbols.items(): if sym == status_filter: filters.append(txt) - for filt_txt in filters: - for pkg in status.keys(): - for repo in status[pkg].keys(): - if status[pkg][repo] == filt_txt: - if not name_filter: - pacs_to_show.append(pkg) - targets_to_show.append(repo) - elif name_filter in pkg: - pacs_to_show.append(pkg) - + else: + filters.append(status_filter) + for filt_txt in filters: + for pkg in status.keys(): + for repo in status[pkg].keys(): + if status[pkg][repo] == filt_txt: + if not name_filter: + pacs_to_show.append(pkg) + targets_to_show.append(repo) + elif name_filter in pkg: + pacs_to_show.append(pkg) #filtering for Package Name elif name_filter: for pkg in pacs: @@ -5956,6 +5974,14 @@ r.append(';'.join(row)) return r + if brief: + for pac, repo_states in status.items(): + for repo, state in repo_states.items(): + if filters and state not in filters: + continue + r.append('%s %s %s %s' % (pac, repo[0], repo[1], state)) + return r + if not vertical: # human readable output max_pacs = 40 @@ -6116,6 +6142,8 @@ def print_data(data, strip_time=False): if strip_time: data = buildlog_strip_time(data) + # hmm calling decode_it is a bit problematic because data might begin + # or end with an, for instance, incomplete utf-8 sequence sys.stdout.write(decode_it(data.translate(all_bytes, remove_bytes))) # to protect us against control characters diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.169.1/osc/fetch.py new/osc-0.170.0/osc/fetch.py --- old/osc-0.169.1/osc/fetch.py 2020-05-29 20:35:00.000000000 +0200 +++ new/osc-0.170.0/osc/fetch.py 2020-07-09 09:57:53.000000000 +0200 @@ -9,11 +9,11 @@ try: from urllib.parse import quote_plus - from urllib.request import HTTPBasicAuthHandler, HTTPCookieProcessor, HTTPPasswordMgrWithDefaultRealm, HTTPError + from urllib.request import HTTPError except ImportError: #python 2.x from urllib import quote_plus - from urllib2 import HTTPBasicAuthHandler, HTTPCookieProcessor, HTTPPasswordMgrWithDefaultRealm, HTTPError + from urllib2 import HTTPError from .core import makeurl, streamfile, dgst from .grabber import OscFileGrabber, OscMirrorGroup @@ -43,13 +43,6 @@ self.cpio = {} self.enable_cpio = enable_cpio - passmgr = HTTPPasswordMgrWithDefaultRealm() - for host in api_host_options: - passmgr.add_password(None, host, api_host_options[host]['user'], - api_host_options[host]['pass']) - openers = (HTTPBasicAuthHandler(passmgr), ) - if cookiejar: - openers += (HTTPCookieProcessor(cookiejar), ) self.gr = OscFileGrabber(progress_obj=self.progress_obj) def __add_cpio(self, pac): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.169.1/osc/util/helper.py new/osc-0.170.0/osc/util/helper.py --- old/osc-0.169.1/osc/util/helper.py 2020-05-29 20:35:00.000000000 +0200 +++ new/osc-0.170.0/osc/util/helper.py 2020-07-09 09:57:53.000000000 +0200 @@ -56,22 +56,19 @@ def decode_it(obj): - """ Decodes the given object if obj is not a string - based on the chardet module if possible - """ + """Decode the given object unless it is a str. - if obj is None or isinstance(obj, str): + If the given object is a str or has no decode method, the object itself is + returned. Otherwise, try to decode the object using utf-8. If this + fails due to a UnicodeDecodeError, try to decode the object using + latin-1. + """ + if isinstance(obj, str) or not hasattr(obj, 'decode'): return obj - else: - try: - import chardet - return obj.decode(chardet.detect(obj)['encoding']) - except: - try: - import locale - return obj.decode(locale.getlocale()[1]) - except: - return obj.decode('latin-1') + try: + return obj.decode('utf-8') + except UnicodeDecodeError: + return obj.decode('latin-1') def raw_input(*args): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.169.1/osc/util/repodata.py new/osc-0.170.0/osc/util/repodata.py --- old/osc-0.169.1/osc/util/repodata.py 2020-05-29 20:35:00.000000000 +0200 +++ new/osc-0.170.0/osc/util/repodata.py 2020-07-09 09:57:53.000000000 +0200 @@ -5,11 +5,13 @@ import gzip import os.path -# cElementTree can be standard or 3rd-party depending on python version try: + # Works up to Python 3.8, needed for Python < 3.3 (inc 2.7) from xml.etree import cElementTree as ET except ImportError: - import cElementTree as ET + # will import a fast implementation from 3.3 onwards, needed + # for 3.9+ + from xml.etree import ElementTree as ET # project modules import osc.util.rpmquery diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.169.1/tests/common.py new/osc-0.170.0/tests/common.py --- old/osc-0.169.1/tests/common.py 2020-05-29 20:35:00.000000000 +0200 +++ new/osc-0.170.0/tests/common.py 2020-07-09 09:57:53.000000000 +0200 @@ -4,12 +4,15 @@ import tempfile import os import sys -from xml.etree import cElementTree as ET +try: + # Works up to Python 3.8, needed for Python < 3.3 (inc 2.7) + from xml.etree import cElementTree as ET +except ImportError: + # will import a fast implementation from 3.3 onwards, needed + # for 3.9+ + from xml.etree import ElementTree as ET EXPECTED_REQUESTS = [] -if sys.version_info[0:2] in ((2, 6), (2, 7)): - bytes = lambda x, *args: x - try: #python 2.x from cStringIO import StringIO @@ -45,6 +48,30 @@ return True + +def xml_equal(actual, exp): + try: + actual_xml = ET.fromstring(actual) + exp_xml = ET.fromstring(exp) + except ET.ParseError: + return False + todo = [(actual_xml, exp_xml)] + while todo: + actual_xml, exp_xml = todo.pop(0) + if actual_xml.tag != exp_xml.tag: + return False + if actual_xml.attrib != exp_xml.attrib: + return False + if actual_xml.text != exp_xml.text: + return False + if actual_xml.tail != exp_xml.tail: + return False + if len(actual_xml) != len(exp_xml): + return False + todo.extend(list(zip(actual_xml, exp_xml))) + return True + + class RequestWrongOrder(Exception): """raised if an unexpected request is issued to urllib2""" def __init__(self, url, exp_url, method, exp_method): @@ -90,15 +117,24 @@ if exp is not None and 'expfile' in kwargs: raise RuntimeError('either specify exp or expfile') elif 'expfile' in kwargs: - exp = open(os.path.join(self.__fixtures_dir, kwargs['expfile']), 'r').read() + exp = open(os.path.join(self.__fixtures_dir, kwargs['expfile']), 'rb').read() elif exp is None: raise RuntimeError('exp or expfile required') - if exp is not None: - # use req.data instead of req.get_data() for python3 compatiblity - data = req.data - if hasattr(data, 'read'): - data = data.read() - if data != bytes(exp, "utf-8"): + else: + # for now, assume exp is a str + exp = exp.encode('utf-8') + # use req.data instead of req.get_data() for python3 compatiblity + data = req.data + if hasattr(data, 'read'): + data = data.read() + if data != exp: + # We do not have a notion to explicitly mark xml content. In case + # of xml, we do not care about the exact xml representation (for + # now). Hence, if both, data and exp, are xml and are "equal", + # everything is fine (for now); otherwise, error out + # (of course, this is problematic if we want to ensure that XML + # documents are bit identical...) + if not xml_equal(data, exp): raise RequestDataMismatch(req.get_full_url(), repr(data), repr(exp)) return self.__get_response(req.get_full_url(), **kwargs) @@ -109,7 +145,7 @@ if 'text' not in kwargs and 'file' in kwargs: f = BytesIO(open(os.path.join(self.__fixtures_dir, kwargs['file']), 'rb').read()) elif 'text' in kwargs and 'file' not in kwargs: - f = BytesIO(bytes(kwargs['text'], 'utf-8')) + f = BytesIO(kwargs['text'].encode('utf-8')) else: raise RuntimeError('either specify text or file') resp = addinfourl(f, {}, url) @@ -193,14 +229,27 @@ def _check_digests(self, fname, *skipfiles): fname = os.path.join(self._get_fixtures_dir(), fname) - self.assertEqual(open(os.path.join('.osc', '_files'), 'r').read(), open(fname, 'r').read()) - root = ET.parse(fname).getroot() + with open(os.path.join('.osc', '_files'), 'r') as f: + files_act = f.read() + with open(fname, 'r') as f: + files_exp = f.read() + self.assertXMLEqual(files_act, files_exp) + root = ET.fromstring(files_act) for i in root.findall('entry'): if i.get('name') in skipfiles: continue self.assertTrue(os.path.exists(os.path.join('.osc', i.get('name')))) self.assertEqual(osc.core.dgst(os.path.join('.osc', i.get('name'))), i.get('md5')) + def assertXMLEqual(self, act, exp): + if xml_equal(act, exp): + return + # ok, xmls are different, hence, assertEqual is expected to fail + # (we just use it in order to get a "nice" error message) + self.assertEqual(act, exp) + # not reached (unless assertEqual is overridden in an incompatible way) + raise RuntimeError('assertEqual assumptions violated') + def assertEqualMultiline(self, got, exp): if (got + exp).find('\n') == -1: self.assertEqual(got, exp) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.169.1/tests/test_commit.py new/osc-0.170.0/tests/test_commit.py --- old/osc-0.169.1/tests/test_commit.py 2020-05-29 20:35:00.000000000 +0200 +++ new/osc-0.170.0/tests/test_commit.py 2020-07-09 09:57:53.000000000 +0200 @@ -3,7 +3,13 @@ import os import sys from common import GET, PUT, POST, DELETE, OscTestCase -from xml.etree import cElementTree as ET +try: + # Works up to Python 3.8, needed for Python < 3.3 (inc 2.7) + from xml.etree import cElementTree as ET +except ImportError: + # will import a fast implementation from 3.3 onwards, needed + # for 3.9+ + from xml.etree import ElementTree as ET try: from urllib.error import HTTPError except ImportError: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.169.1/tests/test_repairwc.py new/osc-0.170.0/tests/test_repairwc.py --- old/osc-0.169.1/tests/test_repairwc.py 2020-05-29 20:35:00.000000000 +0200 +++ new/osc-0.170.0/tests/test_repairwc.py 2020-07-09 09:57:53.000000000 +0200 @@ -3,7 +3,13 @@ import os import sys from common import GET, PUT, POST, DELETE, OscTestCase -from xml.etree import cElementTree as ET +try: + # Works up to Python 3.8, needed for Python < 3.3 (inc 2.7) + from xml.etree import cElementTree as ET +except ImportError: + # will import a fast implementation from 3.3 onwards, needed + # for 3.9+ + from xml.etree import ElementTree as ET FIXTURES_DIR = os.path.join(os.getcwd(), 'repairwc_fixtures') def suite(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-0.169.1/tests/test_request.py new/osc-0.170.0/tests/test_request.py --- old/osc-0.169.1/tests/test_request.py 2020-05-29 20:35:00.000000000 +0200 +++ new/osc-0.170.0/tests/test_request.py 2020-07-09 09:57:53.000000000 +0200 @@ -1,3 +1,11 @@ +try: + # Works up to Python 3.8, needed for Python < 3.3 (inc 2.7) + from xml.etree import cElementTree as ET +except ImportError: + # will import a fast implementation from 3.3 onwards, needed + # for 3.9+ + from xml.etree import ElementTree as ET + import osc.core import osc.oscerr import os @@ -38,7 +46,7 @@ <target package="bar" project="foobar" /> </action> </request>""" - self.assertEqual(exp, r.to_str()) + self.assertXMLEqual(exp, r.to_str()) def test_createsr_with_option(self): """create a submitrequest with option""" @@ -67,7 +75,7 @@ </options> </action> </request>""" - self.assertEqual(exp, r.to_str()) + self.assertXMLEqual(exp, r.to_str()) def test_createsr_missing_tgt_package(self): """create a submitrequest with missing target package""" @@ -88,7 +96,7 @@ <target project="foobar" /> </action> </request>""" - self.assertEqual(exp, r.to_str()) + self.assertXMLEqual(exp, r.to_str()) def test_createsr_invalid_argument(self): """create a submitrequest with invalid action argument""" @@ -117,7 +125,7 @@ <person name="user" role="reader" /> </action> </request>""" - self.assertEqual(exp, r.to_str()) + self.assertXMLEqual(exp, r.to_str()) def test_create_add_role_group(self): """create an add_role request (group element)""" @@ -138,7 +146,7 @@ <group name="group" role="reviewer" /> </action> </request>""" - self.assertEqual(exp, r.to_str()) + self.assertXMLEqual(exp, r.to_str()) def test_create_add_role_person_group(self): """create an add_role request (person+group element)""" @@ -161,7 +169,7 @@ <group name="group" role="reviewer" /> </action> </request>""" - self.assertEqual(exp, r.to_str()) + self.assertXMLEqual(exp, r.to_str()) def test_create_set_bugowner_project(self): """create a set_bugowner request for a project""" @@ -179,7 +187,7 @@ <person name="buguser" /> </action> </request>""" - self.assertEqual(exp, r.to_str()) + self.assertXMLEqual(exp, r.to_str()) def test_create_set_bugowner_package(self): """create a set_bugowner request for a package""" @@ -197,7 +205,7 @@ <person name="buguser" /> </action> </request>""" - self.assertEqual(exp, r.to_str()) + self.assertXMLEqual(exp, r.to_str()) def test_create_delete_project(self): """create a delete request for a project""" @@ -213,7 +221,7 @@ <target project="foo" /> </action> </request>""" - self.assertEqual(exp, r.to_str()) + self.assertXMLEqual(exp, r.to_str()) def test_create_delete_package(self): """create a delete request for a package""" @@ -229,7 +237,7 @@ <target package="deleteme" project="foo" /> </action> </request>""" - self.assertEqual(exp, r.to_str()) + self.assertXMLEqual(exp, r.to_str()) def test_create_change_devel(self): """create a change devel request""" @@ -248,11 +256,10 @@ <target package="devpkg" project="devprj" /> </action> </request>""" - self.assertEqual(exp, r.to_str()) + self.assertXMLEqual(exp, r.to_str()) def test_action_from_xml1(self): """create action from xml""" - from xml.etree import cElementTree as ET xml = """<action type="add_role"> <target package="bar" project="foo" /> <person name="user" role="reader" /> @@ -266,11 +273,10 @@ self.assertEqual(action.person_role, 'reader') self.assertEqual(action.group_name, 'group') self.assertEqual(action.group_role, 'reviewer') - self.assertEqual(xml, action.to_str()) + self.assertXMLEqual(xml, action.to_str()) def test_action_from_xml2(self): """create action from xml""" - from xml.etree import cElementTree as ET xml = """<action type="submit"> <source package="bar" project="foo" /> <target package="bar" project="foobar" /> @@ -288,11 +294,10 @@ self.assertEqual(action.opt_sourceupdate, 'cleanup') self.assertEqual(action.opt_updatelink, '1') self.assertTrue(action.src_rev is None) - self.assertEqual(xml, action.to_str()) + self.assertXMLEqual(xml, action.to_str()) def test_action_from_xml3(self): """create action from xml (with acceptinfo element)""" - from xml.etree import cElementTree as ET xml = """<action type="submit"> <source package="bar" project="testprj" /> <target package="baz" project="foobar" /> @@ -312,17 +317,15 @@ self.assertEqual(action.acceptinfo_xsrcmd5, 'ffffffffffffffffffffffffffffffff') self.assertTrue(action.acceptinfo_osrcmd5 is None) self.assertTrue(action.acceptinfo_oxsrcmd5 is None) - self.assertEqual(xml, action.to_str()) + self.assertXMLEqual(xml, action.to_str()) def test_action_from_xml_unknown_type(self): """try to create action from xml with unknown type""" - from xml.etree import cElementTree as ET xml = '<action type="foo"><source package="bar" project="foo" /></action>' self.assertRaises(osc.oscerr.WrongArgs, osc.core.Action.from_xml, ET.fromstring(xml)) def test_read_request1(self): """read in a request""" - from xml.etree import cElementTree as ET xml = open(os.path.join(self._get_fixtures_dir(), 'test_read_request1.xml'), 'r').read().strip() r = osc.core.Request() r.read(ET.fromstring(xml)) @@ -350,11 +353,10 @@ self.assertEqual(r.description, 'this is a\nvery long\ndescription') self.assertTrue(len(r.statehistory) == 1) self.assertTrue(len(r.reviews) == 0) - self.assertEqual(xml, r.to_str()) + self.assertXMLEqual(xml, r.to_str()) def test_read_request2(self): """read in a request (with reviews)""" - from xml.etree import cElementTree as ET xml = open(os.path.join(self._get_fixtures_dir(), 'test_read_request2.xml'), 'r').read().strip() r = osc.core.Request() r.read(ET.fromstring(xml)) @@ -389,11 +391,10 @@ self.assertEqual(r.creator, 'creator') self.assertTrue(len(r.statehistory) == 1) self.assertTrue(len(r.reviews) == 1) - self.assertEqual(xml, r.to_str()) + self.assertXMLEqual(xml, r.to_str()) def test_read_request3(self): """read in a request (with an "empty" comment+description)""" - from xml.etree import cElementTree as ET xml = """<request creator="xyz" id="2"> <action type="set_bugowner"> <target project="foo" /> @@ -426,11 +427,10 @@ <state name="new" when="2010-12-28T12:36:29" who="xyz" /> </request>""" - self.assertEqual(exp, r.to_str()) + self.assertXMLEqual(exp, r.to_str()) def test_request_list_view1(self): """test the list_view method""" - from xml.etree import cElementTree as ET xml = open(os.path.join(self._get_fixtures_dir(), 'test_request_list_view1.xml'), 'r').read().strip() exp = """\ 62 State:new By:Admin When:2010-12-29T14:57:25 @@ -448,7 +448,6 @@ def test_request_list_view2(self): """test the list_view method (with history elements and description)""" - from xml.etree import cElementTree as ET xml = open(os.path.join(self._get_fixtures_dir(), 'test_request_list_view2.xml'), 'r').read().strip() r = osc.core.Request() r.read(ET.fromstring(xml)) @@ -462,7 +461,6 @@ self.assertEqual(exp, r.list_view()) def test_request_str1(self): - from xml.etree import cElementTree as ET """test the __str__ method""" xml = open(os.path.join(self._get_fixtures_dir(), 'test_request_str1.xml'), 'r').read().strip() r = osc.core.Request() @@ -496,7 +494,6 @@ def test_request_str2(self): """test the __str__ method""" - from xml.etree import cElementTree as ET xml = """\ <request creator="creator" id="98765"> <action type="change_devel"> @@ -527,7 +524,6 @@ def test_legacy_request(self): """load old-style submitrequest""" - from xml.etree import cElementTree as ET xml = """\ <request creator="olduser" id="1234" type="submit"> <submit> @@ -559,11 +555,10 @@ </action> <state name="new" when="2010-12-30T02:11:22" who="olduser" /> </request>""" - self.assertEqual(exp, r.to_str()) + self.assertXMLEqual(exp, r.to_str()) def test_get_actions(self): """test get_actions method""" - from xml.etree import cElementTree as ET xml = open(os.path.join(self._get_fixtures_dir(), 'test_request_list_view1.xml'), 'r').read().strip() r = osc.core.Request() r.read(ET.fromstring(xml)) ++++++ osc.dsc ++++++ --- /var/tmp/diff_new_pack.Y2jfES/_old 2020-07-09 13:22:07.221791633 +0200 +++ /var/tmp/diff_new_pack.Y2jfES/_new 2020-07-09 13:22:07.221791633 +0200 @@ -1,6 +1,6 @@ Format: 1.0 Source: osc -Version: 0.169.1 +Version: 0.170.0 Binary: osc Maintainer: Adrian Schroeter <adr...@suse.de> Architecture: any