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


Reply via email to